Läsa från två filer… [LÖST]

Här diskuterar vi skal, kommandon och klassiska linuxverktyg.
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Läsa från två filer… [LÖST]

Inlägg av Johnny Rosenberg »

Detta förstår jag:

Kod: Markera allt

while read something   
do   
	blah blah blah
done < "$HOME/Blahoo"
Men jag vill läsa från två filer. Den ena innehåller ett antal rader med ett ord i varje rad och den andra innehåller samma antal rader men där varje rad är en ”översättning” av den första filen. Det kan vara olika språk eller olika blandning av gemener och versaler eller i princip vad som helst.

Vad jag sedan vill göra är att söka igenom en tredje lista efter första raden i första listan och för varje förekomst ersätta det med första raden i den andra listan. Möjligt? Hur?

Som exempel kan vi säga att vi har tre filer. Fil 1:
1
2
3
farbrorn
Fil 2:
ett
tu
tre
tanten
Fil 3:
Och 1 2 3, så hade farbrorn ramlat i sjön.
Efter att skriptet körts ska då fil 3 se ut så här, som säkert alla redan listat ut:
Och ett tu tre, så hade tanten ramlat i sjön.
Kom tyvärr inte på något ännu löjligare exempel…
Senast redigerad av 1 Johnny Rosenberg, redigerad totalt 9 gånger.
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Substrata
Inlägg: 71
Blev medlem: 13 apr 2010, 11:01
OS: Arch Linux
Utgåva: Vet inte/ingen utgåva passar

Re: Läsa från två filer…

Inlägg av Substrata »

Även om problemet är intressant så finns det en anledning till att jag drar mig för att shellscripta. Här följer en långt från perfekt lösning att begrunda. Det kan finnas fler fel än jag själv anmärker längre ned.

Kod: Markera allt

#!/bin/bash

from=$1
to=$2
r=$3

# takes a word
#  get the line# of the word from $from
# gives either
#  contents of line# of $to if the word is found
# or
#  the input word (no transformation)
transform() {
    n=$(grep -n $1 $from | cut -d: -f1)
    if ((${#n} > 0))
    then head -$n $to | tail -1
    else echo $1
    fi
}

for n in $(<$r)
do echo -n "$(transform $n) "
done
Körningen, med tre filer enligt ditt exempel.

Kod: Markera allt

% ./r0.bash r0.?
Och ett tu 3, så hade tanten ramlat i sjön. % 
Anledningen till att "3" inte översätts är att for-loopen ger den som "3,", så man behöver knåda bort kommatecken och andra satsdelare, för att efteråt sätta tillbaka mot det översatta ordet. Sedan är det ett fultecken på slutet, av nån anledning.
Användarvisningsbild
dmz
Inlägg: 3292
Blev medlem: 29 jul 2008, 19:42
OS: Arch Linux

Re: Läsa från två filer…

Inlägg av dmz »

Jag vill inte ens tänka tanken på hur det hade gått om jag hade försökt med bash...
Vi mappar helt enkelt orden i den första listan, nyckarna, till respektive ord, värden, i den andra.
Sedan ersätter vi nycklarna med värdena och skriver resultatet till en fjärde fil.

Kod: Markera allt

#!/usr/bin/perl
use strict;

my $file1 = shift // 'file1';
my $file2 = shift // 'file2';
my $file3 = shift // 'file3';
my $file4 = shift // 'file4';

open(my $fh1, '<', $file1) or die($!);
open(my $fh2, '<', $file2) or die($!);

chomp(my @content_keys   = <$fh1>);
chomp(my @content_values = <$fh2>);

close($fh1);
close($fh2);

my %map = ();
@map{@content_keys} = @content_values;

open(my $fh, '<', $file3) or die($!);
chomp(my @data = <$fh>);
close($fh);

my $modified = 0;
for(@data) {
  for my $k(keys(%map)) {
    $_ =~ s/$k/$map{$k}/ and $modified++;
  }
}

if($modified) {
  open(my $fh, '>', $file4) or die($!);
  print $fh @data;
  close($fh);
}
@Substrata: Anledningen till % (som inte hör till outputten) är att det är zsh's default-char för att markera att det inte finns någon newline (istället för att förstöra prompten så som bash gör).
Jfr. printf "foo", printf "foo\n".
ǁ A: Because it obfuscates the reading.
ǁ Q: Why is top posting so bad?
Användarvisningsbild
Substrata
Inlägg: 71
Blev medlem: 13 apr 2010, 11:01
OS: Arch Linux
Utgåva: Vet inte/ingen utgåva passar

Re: Läsa från två filer…

Inlägg av Substrata »

En bättre lösning, men det kvarstår ett par problem även här. Det blir fel om man bara stegar över nycklarna; man behöver också stega över orden i strängen. För att ge exempel: Givet "111 2 3" så ges "ett11 tu tre". Det är också möjligt att ha överlappande översättningar ("1" -> "ett", "11" -> "elva").

Jag landade här, med reservation för smidigare sätt att traversera en sträng med regular expressions. Lade också om så att scriptet använder pipes.

Kod: Markera allt


#!/usr/bin/perl
use strict;

my $file1 = shift // 'file1';
my $file2 = shift // 'file2';

open(my $fh1, '<', $file1) or die($!);
open(my $fh2, '<', $file2) or die($!);

chomp(my @content_keys   = <$fh1>);
chomp(my @content_values = <$fh2>);

close($fh1);
close($fh2);

my %map = ();
@map{@content_keys} = @content_values;

while (<stdin>) {
  chomp;
  my $out = '';
  until (!/(\w+)+/p) {
    $out = sprintf "%s%s%s", $out, ${^PREMATCH}, exists $map{$1}? $map{$1}: $1;
    $_ = ${^POSTMATCH};
  };
  print "$out$_\n";
}
Testkörning.

Kod: Markera allt

% ./r0.pl r0.1 r0.2 < r0.3
Och ett tu tre, så hade tanten ramlat i sjön.
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Läsa från två filer…

Inlägg av Johnny Rosenberg »

Oj, det verkar som att det var lite lurigare än vad jag trodde, det där. Har dock kommit på att jag kanske skapade ett problem som egentligen inte fanns från början. Enklare blir det ju om man inte använder två filer för ”översättningen”, utan bara en enda. Om det enbart rör sig om enstaka ord, som i mitt fall, så borde det ju gå att använda en fil som ser ut så här:
1 ett
2 två
3 tre
farbrorn tanten
Sedan läsa en rad och låta mellanslaget agera separator, alternativt kanske ↹. Eller så kan man kanske ha en fil som ser ut så här:
1
ett
2
två
3
tre
farbrorn
tanten
Men detta ser ju mycket rörigare och mer svårläst ut, vilket är en nackdel om man vill redigera filen manuellt i en vanlig textredigerare.

Tack för engagemanget i alla fall, lärorikt och intressant. Vet dock inte om just Perl står på tur när det gäller vad mer nytt jag vill lära mig i livet. Så mycket att lära, så lite tid…

Har som en liten princip att när jag skriver något program, skript eller vad det nu kan vara, så vill jag förstå vad som händer, helst i detalj. Är inte så mycket för att planka av andras arbete rakt av, så om jag håller på med något litet projekt så ställer jag sällan en fråga på ett sådant sätt att någon annans svar är det färdiga projektet.

Ofta fastnar man ju på något litet delproblem och då hittar jag oftast på ett annat problem där samma delproblem ingår, så att säga.
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Substrata
Inlägg: 71
Blev medlem: 13 apr 2010, 11:01
OS: Arch Linux
Utgåva: Vet inte/ingen utgåva passar

Re: Läsa från två filer…

Inlägg av Substrata »

Johnny Rosenberg skrev:Enklare blir det ju om man inte använder två filer för ”översättningen”, utan bara en enda. [...] Sedan läsa en rad och låta mellanslaget agera separator [...]
Egentligen blir det inte så mycket enklare. Problemet har en given komplexitet. Om du lägger allt i en fil så behöver du också logik för att hantera att varannat ord är "från" och varannat är "till". En fil kan dock favoriseras om du har väldigt många ord, eftersom man ser på rak arm vilken översättning som är aktuell.
Men detta ser ju mycket rörigare och mer svårläst ut, vilket är en nackdel om man vill redigera filen manuellt i en vanlig textredigerare.
En idé är att ha något i stil med

Kod: Markera allt

1
ett

2
tu
Den extra raden gör det inte mer komplicerat om bara använder tomrum som separator. Shellet kan fixa det åt dig.

En fundering jag hade är behovet att översätta hela fraser, där tomrum ingår. Mina exempel hanterar exempelvis inte det, och bilden jag har av en sådan lösning är något svårare.
Tack för engagemanget i alla fall, lärorikt och intressant. Vet dock inte om just Perl står på tur när det gäller vad mer nytt jag vill lära mig i livet. Så mycket att lära, så lite tid…
Standardverktyget awk(1) kan nog hjälpa dig framåt i det här fallet. Sånt som administratörer brukar vilja kunna.
Har som en liten princip att när jag skriver något program, skript eller vad det nu kan vara, så vill jag förstå vad som händer, helst i detalj. Är inte så mycket för att planka av andras arbete rakt av, så om jag håller på med något litet projekt så ställer jag sällan en fråga på ett sådant sätt att någon annans svar är det färdiga projektet.
Kudos, och det är bara kul med någon nöt ibland. :)
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Läsa från två filer…

Inlägg av Johnny Rosenberg »

Jag försökte lösa det genom att låta listan innehålla ord/fras och översättning på samma rad med \t som separator. Jag läser då en rad och mejslar ut de respektive orden (ej fraser i mitt fall, men det borde också gå bra, tycker jag spontant) enligt följande ($Line är hela raden):

Kod: Markera allt

Test=$(echo -e $Line | sed 's/\t.*//')
Replace=$(echo -e $Line | sed 's/.*\t//')
Därefter räcker det ju med något i stil med följande utdrag ur det skript jag till sist försökt nedteckna – ett skript som ”översätter” filnamn:

Kod: Markera allt

find "$MyPath" -type f -exec rename -v 's;$Test;$Replace;g;' {} +
Men jag har inte vågat testa än, så jag kanske är helt ute och cyklar som vanligt…

Men nu måste jag nog sova om jag ska orka cykla till jobbet imorgon…

;D

Men det är rätt kul det här ändå, tycker jag. Så här kul hade jag ju aldrig när jag hade Windows, vad jag kan komma ihåg.

Här är skriptet i sin helhet förresten, otestat som sagt:

Kod: Markera allt

#!bin/bash

File="Egennamn-egennamn"
Result="Namnbytestest"
Options="-v"
MyPath="$HOME/Eget/Media/"

if [ -f "$Result" ]; then
	rm "$Result"
fi

# Inledande versal.
find "$MyPath" -type f -exec rename $Options 's;(.*/)(.)(.+);$1\U$2\E$3;g;' {} +  > "$Result"

# Inledande versal för å, ä och ö.
for x in å ä ö
do
	find "$MyPath" -type f -exec rename "$Options" 's;(.*/)$x(.+);$1\U$x\E$2;g;' {} +  >> "$Result"
done

# Versaler och gemener i egennamn enligt listan i ”File”.
while read Line
do   
	Test=$(echo -e $Line | sed 's/\t.*//')
	Replace=$(echo -e $Line | sed 's/.*\t//')

	find "$MyPath" -type f -exec rename "$Options" 's;$Test;$Replace;g;' {} +  >> "$Result"
done < "$File"
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Läsa från två filer…

Inlägg av Konservburk »

Johnny Rosenberg skrev:Detta förstår jag:

Kod: Markera allt

while read something   
do   
	blah blah blah
done < "$HOME/Blahoo"
Men jag vill läsa från två filer.
Då kan du göra så här:

Kod: Markera allt

while read a <&3 && read b <&4
do echo "$a" : "$b"
done 3< filA 4< filB
Det som händer är att du talar om för read att den ska läsa till variabeln $a från fildeskriptor nummer 3 och till variabeln $b från fildeskriptor nummer 4. Vilka filer som hör till dessa båda fildeskriptorer anger du direkt efter done.
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Läsa från två filer…

Inlägg av Johnny Rosenberg »

Konservburk skrev:
Johnny Rosenberg skrev:Detta förstår jag:

Kod: Markera allt

while read something   
do   
	blah blah blah
done < "$HOME/Blahoo"
Men jag vill läsa från två filer.
Då kan du göra så här:

Kod: Markera allt

while read a <&3 && read b <&4
do echo "$a" : "$b"
done 3< filA 4< filB
Det som händer är att du talar om för read att den ska läsa till variabeln $a från fildeskriptor nummer 3 och till variabeln $b från fildeskriptor nummer 4. Vilka filer som hör till dessa båda fildeskriptorer anger du direkt efter done.
Tackar för det svaret. Då kanske det inte är så krångligt i alla fall, med andra ord. Någon särskild anledning att välja just 3 och 4? Är 1 och 2 reserverade till annat?

Har ju redan löst det genom att enbart använda en fil som innehåller all information som de två gamla tillsammans skulle innehållit där informationen separeras från varandra med ett ↹-tecken (\t), men detta kan säkert komma till användning en annan gång och under alla omständigheter lärde jag mig ju något nytt också.
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Läsa från två filer…

Inlägg av Konservburk »

Johnny Rosenberg skrev:Tackar för det svaret. Då kanske det inte är så krångligt i alla fall, med andra ord. Någon särskild anledning att välja just 3 och 4? Är 1 och 2 reserverade till annat?
Ja, 0 är stdin, 1 är stdout och 2 är stderr, så 3 och 4 är de första två som är lediga.
Skriv svar

Återgå till "Terminalforum"