• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

If in einer Endlosschleife geht nicht

Hallo,

ich habe hier folgendes Script.

Code:
file=/path/to/file
while true ; do
count=$(cat "$file" | wc -m )
 if [ "$count" -ge 1 ] ; then
  while IFS= read -r zeile ; do 
   echo "$zeile"
   sed -i '1,1 d' "$file"
  done < "$file"
 else
  echo "nichts zu tun"
 fi
done

In die Variable 'count' wird geschrieben wieviel Wörter in einer Textdatei stehen. Mit If wird geprüft ob die Anzahl der Wörter größer 0 ist, wenn ja sollen die Zeilen der Datei ausgegeben und danach gelöscht werden. Falls die Datei leer ist wird angezeigt das nichts zu tun ist (eigentlich soll es dann beendet werden, hab ich für die Versuche geändert). Das ganze steckt in einer Endlosschleife die so lange läuft bis die Datei leer ist.

Problem: If scheint sich anders zu verhalten wenn es in der Endlosschleife steckt oder wenn es "allein" steht. Kommentiere ich die Endlosschleife aus, werden mir alle Zeilen der Datei angezeigt, wenn If in der Endlosschleife steckt, wird "nichts zu tun" angezeigt, auch wenn er ohne Endlosschleife die Datei auswertet. Wie kann das sein?
 

StephanS

Member
Funktioniert bei mir. Beim ersten Durchgang zeigt er die Datei zeilenweise an und löscht dabei alle Zeilen. Beim zweiten und allen folgenden Durchgängen ist die Datei leer und das Skript zeigt korrekterweise "nichts zu tun" an.
 

framp

Moderator
Teammitglied
Funktioniert bei mir auch. Anbei ein Vorschlag wie ich es schreiben wuerde:
Bash:
#!/bin/bash

file=$1
while true ; do
#count=$(cat "$file" | wc -m )
 count=$(wc -m < "$file" )
#if [ "$count" -ge 1 ] ; then
 if (( "$count" >=  1 )) ; then
  while IFS= read -r zeile ; do
   echo "$zeile"
   sed -i '1,1 d' "$file"
  done < "$file"
 else
  echo "nichts zu tun"
 fi
done
 
OP
B

bunter fisch

Member
Hatte einen Fehler drin. Es wurde sehr oft "nichts zu tun" ausgegeben, aber gaaanz oben im Terminal stand die Ausgabe drin. Hatte ich nicht gesehen.

Ich hab aber noch ein anderes Problem. Wenn ich die Sache etwas abwandle und statt eien Zeile nur mit echo anzeigen diese ausführen lasse (wenn die Zeile ein Kommando enthält) geht das, solange kein && drin ist. Angenommen die Zeile in der Datei sieht so aus:

Code:
program --input filename --options --output

geht das. Wenn der aber so aussieht

Code:
program --input filename --options --output && mv output output_mod

wird ständig eine 0 Byte große Datei erstellt. Scheinbar wird das && ignoriert und 'mv' schon ausgeführt bevor 'program' fertig ist. Kopiere ich die Zeile in ein Terminal geht alles wie es soll.
 
OP
B

bunter fisch

Member
Wovon? Ist das gleiche Script wie oben, nur ohne echo:

Code:
file=$1
while true ; do
count=$(cat "$file" | wc -m )
if [ "$count" -ge 1 ] ; then
  while IFS= read -r zeile ; do
   "$zeile"
   sed -i '1,1 d' "$file"
  done < "$file"
 else
  echo "nichts zu tun"
 fi
done

Anstatt die Zeile anzuzeigen wird sie ausgeführt - oder sollte zumindest. Oder was für Code meinst du?
 

abgdf

Guru
Mir gefällt dabei nicht, innerhalb einer Schleife, die sich auf eine bestimmte Datei bezieht, mit sed auf derselben Datei rumzuhacken (oder die Datei sonst zu verändern) (Dein "read" kann z.B. versuchen, Zeilen zu lesen, die sed gerade gekillt hat). Das kann bei der Prüfung der Bedingung für die Schleife zu unkontrollierten Ergebnissen führen.
Das zusammen mit der Endlosschleife drumrum macht dann die Verwirrung komplett. ;) Dann wird es auch schwer, den Fehler zu finden. Weil er nicht direkt aus dem Code ablesbar ist, sondern vom Inhalt der verarbeiteten Datei abhängt. So ein Programm kann dann mal laufen, muß aber nicht.
Das alles sollte man vermeiden.

Warum überhaupt zeilenweise? Wenn "wc -m", das sich ja auf die ganze Datei bezieht, was anzeigt, kannst Du auch mit "cat" die Datei ganz ausgeben und sie danach ganz löschen. Oder ist es zeilenweise nötig? Warum?
 
A

Anonymous

Gast
In Linux lässt sich eine Datei nicht innerhalb eines Befehls auf der Konsole lesen und schreiben, auch nicht wenn es mehrere Befehle sind die mittels Pipe oÄ.. verknüpft sind.
Code:
LINUX> cat test.txt  > test.txt
cat: test.txt: Eingabedatei ist Ausgabedatei
selbst wenn es scheinbar mal funktioniert und die Shell es nicht erkennt und die Ausführung zuläßt, ist die Datei anschließend in der Regel leer.
Code:
LINUX> ls -il test.txt
3017895 -rw-r--r-- 1 robi users 900  3. Okt 22:52 test.txt
LINUX> cat test.txt | cat | cat | cat | cat > test.txt
LINUX> ls -il test.txt
3017895 -rw-r--r-- 1 robi users 0  3. Okt 22:53 test.txt

somit kann auch "sed -i" nicht einfach eine Datei ändern. ( genauso wie jeder andere Editor auch, legt sed eine andere Datei an und gibt ihr den original Namen, die originale Datei wird dabei gelöscht.
Zu erkennen an den Inodenummer der Datei, die ändern sich bei einem "sed -i " Aufruf.

Code:
LINUX> ls -il test.txt
3017864 -rw-r--r-- 1 robi users 2662  3. Okt 21:49 test.txt
LINUX> sed -in '/16/d' test.txt
LINUX> ls -il test.txt
3017895 -rw-r--r-- 1 robi users 2276  3. Okt 21:51 test.txt

Von da her ist dein Script schon im Ansatz unberechenbar und als ganzes ein Bug. Da du während der Ausführung eine Datei die du zum Lesen geöffnet hast durch die Bearbeitung mittels sed löscht und eine neue mit gleichem Namen anlegst, aber weiterhin aus der Orginaldatei (auch wenn schon im Dateisystem gelöscht) durch den immer noch offenen Filehandle und den ebenfalls offenen Cache der Datei vom Prozesses weiter ausließt, und in einer Schleife mit jedem weiterem Aufruf eine vorher schon geänderte Kopie vom letzten Durchlauf mit sed weiter bearbeitest und zwar auf Datenbasis der Orginaldatei.
Je nach dem wie schnell hier Teilprozesse im Script arbeiten, ist es sogar möglich das der Dateiname gelegentlich mal in einem Schleifendurchlauf gar nicht vorhanden ist, bzw unerwartet leer ist. Da innerhalb der Schleife der Name der Datei ständig auf eine andere Inode zeigt, kann sowas durchaus auch noch zu weiteren unberechenbaren Ergebnissen führen, wenn da nicht irgend ein "wait" zB. durch irgendwelche echo-Befehle innerhalb der Schleife dazu führt, das eine kleine Pause zwischen zwei sed Befehlen liegt die auf den gleichen Dateinamen lesen und schreiben .

robi
 
Oben