ubuntuusers.de

Bash-Skript: Datei zeilenweise einlesen

Status: Gelöst | Ubuntu-Version: Ubuntu 10.04 (Lucid Lynx)
Antworten |

Jay1980

Anmeldungsdatum:
11. Juni 2007

Beiträge: 65

Servus,

das gibts doch nicht, dass ich da keine vorgefertigte Lösung finde: Ich habe eine Datei, die ich gern via Shellskript zeilenweise einlesen will, welchen Befehl muss ich dafür nutzen. Kann mir jemand sagen, wie der Codeschnippsel lautet, um die Datei 'dateiliste.txt' einzulesen und jeweils den Dateinamen auf der Kommandozeile zu echoen?

Die Datei 'dateiliste.txt' hat 3 Einträge, also etwa: meinedateieins.txt meinedateizwei.txt meinedateidrei.txt

Danke vorab für den ein oder anderen Wink in die richtige Richtung!

tobi81

Anmeldungsdatum:
9. Juni 2010

Beiträge: 234

Wohnort: 127.0.0.1

Hallo,

head -n $i dateiliste.txt | tail -n 1

gibt Dir Zeile i aus. Das packst Du einfach in eine for-Schleife mit i als Laufvariable.

Die Anzahl der Zeilen kannst Du mit

wc -l dateiliste.txt

ermitteln.

Gruß tobi81

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17630

Wohnort: Berlin

1
2
3
4
while read line 
do 
   echo "$line" 
done < dateiliste.txt

Wäre offenbar auch unter Shell nicht auffindbar gewesen, obwohl da schon einiges verlinkt ist. ☹

mideal

Anmeldungsdatum:
2. Oktober 2012

Beiträge: 25

Wenn ich beim do die Variable line nicht in Hochkomma setze, läuft das bei mir nicht richtig. Zeilen mit Leerzeichen werden immer zerhackt.

Skriptbeispiel:

1
2
3
4
5
6
7
Beispiel
#! /bin/bash
find ~ -name "*.XYZZ" >  /tmp/listeiso
while read line;
   do ln -s "$line" /home/dirk/Links/ISO;
      echo $line;
done   < /tmp/listeiso

Ausgaben dazu (es gibt nur eine Datei des Musters "*.XYZZ", nämlich "test 123.XYZZ"):

1
2
3
4
dirk@dirk ~/Links $ . ~/Links/Link_anlegen 
/home/dirk/test 123.XYZZ
dirk@dirk ~/Links $ ls /home/dirk/Links/ISO/
test 123.XYZZ

Ausgabe ohne die Hochkomma:

1
2
3
4
5
dirk@dirk ~/Links $ ls /home/dirk/Links/ISO/
dirk@dirk ~/Links $ . ~/Links/Link_anlegen 
/home/dirk/test 123.XYZZ
dirk@dirk ~/Links $ ls /home/dirk/Links/ISO/
'''123.XYZZ  test'''

Das ist so sehr ungünstig, da ich mit ln einen Verweis anlege. Im zweiten Fall werden zwei angelegt - beide fehlerhaft. Ich finde auf keiner Skriptseite einen Hinweis auf dieses Verhalten mit den Hochkomma und dem Teilen der Zeilen.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17630

Wohnort: Berlin

Der Chor der Shelltanten tritt auf.

 Chor: Niemals, niemals, niemals 
 Gurubariton: Außer wir wissen, wissen, wissen
 Chor: niemals, niemals, niemals 
 Gurubariton: sehr genau was wir tun 
 Chor: niemals, niemals, niemals
 Sopran: Early in the morning! 
 Chor: Parsen wir, den output von ls
 Sopran: Late in the evening! 
 Chor: Niemals, niemals, nicht!
 Gurubariton: Und wenn Du doch find hast! 
 Gurubariton: Find hat alles was Du brauchst!
1
find . -name "*.txt" -exec echo ln -s {} /home/dirk/Links/ISO ";" -print 

Leerstellen (und schlimmeres) in Dateinamen und Pfaden müssen in der Shell (aber nicht in Gnu-find) maskiert werden. Daher, wenn möglich, wie hier in find abhandeln und nicht rauskleckern und dann in die Shell wieder reinpfriemeln.

Dass Du das, was Du aus einer Datei liest, interpretierst kann niemand wissen.

1
ln -s A B

erwartet eine Quelle A und ein Ziel B.

1
ln -s a b c 

Soll das als ln -s "a b" c oder als ln -s a "b c" interpretiert werden? Woher soll ln wissen, $line nicht 2 Parameter, 3 Parameter oder was sind? Woher soll read wissen, dass es sich um Dateipfade handelt, die Du maskiert brauchst?

Wenn Deine Datei einen Backslash enthält fällt auch die Maskierung auf die Nase:

1
./b/test/te2\st.txt

Gurubariton: Man kann sowas machen, wenn die Dateinamen von einem selbst angelegt werden, und man Blanks u.ä. konsequent vermieden hat, oder von einem Programm, das man kennt (Kamerabilder die immer CIMG-NNNNN.JPG heißen mit NNNNN als fortlaufende Nummer). Heikel ist es, wenn die Dateien aus unbekannter Quelle stamme, womöglich automatisch aus Textüberschriften generiert sind und alles mögliche enthalten, noch heikler, wenn sie ein Dritter, der um Dein Programm weiß, gezielt anlegen kann und als Einfallstor für Malware oder Sabotage nutzen kann. Am schlimmsten, wenn Du es als Routine an Dritte weitergibst, die die Fallstricke nicht kennen und das Programm für diese heiklen Zwecke einsetzen.

Jedenfalls ist es ein Phänomen des Shellparsing, welches immer wieder auftreten kann. Üblicherweise wird Whitespace vom Tokenizer als Trenner für einzelne Token interpretiert. Das kann man nicht bei jedem Befehl dazuschreiben.

babu59

Anmeldungsdatum:
21. Februar 2016

Beiträge: 1

Da die "for"-Schleife die Eingabe an "whitespaces" bricht entsteht an jedem Whitespace ein neues Element der Liste. Das kann man mit einem Trick umgehen, indem man die "whitespaces" für die "for"-Schleife in "non whitepaces" übersetzt und dann drinnen wieder zurückübersetzt. Das kleine Beispiel hier macht das für "echte" Leerzeichen im Dateinamen:

for i in $(ls -1 *mpg|tr -t " " "~")

do echo "Datei ⇒ $(echo $i|tr -t "~" " ")"

done

In der "for"-Zeile selbst wird der gesamte Inhalt des "ls"-Befehls erst übergeben, nachdem mit dem "tr"-Befehl alle echten Leerzeichen durch die Tilde "~" ersetzt wurden. Das muss ein Zeichen sein, was nicht in der Ergebnisliste vorkommt, will man damit später weiterarbeiten. Da das "ls -1" Kommando eine Datei je Zeile listet, bleiben die Zeilenumbrüche als "whitespaces" erhalten. Daran bricht nun das "for" den Datenstrom. In der "do"-Anweisung demonstriert das Beispiel, dass die Teilstücke, die von "for" als Variable nach innen gegeben werden ebenfalls mit "tr -t" Befehl mit zurückübersetzten Leerzeichen am Stück den Originalnamen ergeben.

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

Hi babu59,

zuerst mal: herzlich willkommen hier auf dem Forum !

Dein Vorschlag ist allerdings ziemlich ungünstig, denn die Ausgabe von ls zu parsen ist ein No-go !
Denn selbstverständlich sind "~" und auch alle anderen Zeichen (außer "/" und 0x00) zulässig in Datei- und Pfadnamen.

Ist auch völlig überflüssig, denn

1
2
3
for i in *mpg ; do
    echo "Datei: [$i]"
done

liefert ganz von selber und ganz sauber alle Dateinamen und ist wasserdicht.

LG,

track

Antworten |