Bash-Syntaxfehler, wenn "else" auf eine leere "then" -Klausel folgt

36

Warum wird das folgende Skript nicht ausgeführt, es wird jedoch der folgende Syntaxfehler ausgegeben else:

LOGS3_DIR=~/logs
if [ -d "$LOGS3_DIR" ]; then
 cd
 cd "$LOGS3_DIR"
 echo "$LOGS3_DIR"
 for filename in `find "." -mtime 1 -type f`
  do
  if lsof "$filename" > /dev/null
  then
    # file is open
  else
    echo "deleting $filename"
    rm "$filename"
  fi
 done
fi
Anfänger
quelle

Antworten:

23

Verwenden Sie keine Befehlsersetzung für die Ausgabe vonfind . Hier kann alles gemacht werden mit find:

find . -mtime 1 -type f ! -exec lsof -t {} \; -exec rm -f {} \; > /dev/null

Mit ein paar findImplementierungen (einschließlich FreeBSD, findwoher es kommt, und GNU find) können Sie -deleteanstelle von verwenden -exec rm....

Der Grund, warum Sie eine Fehlermeldung erhalten, besteht darin, dass zwischen thenund elseund einigen Shells (beginnend mit der Bourne-Shell, von der diese Syntax stammt) kein Befehl vorhanden ist (und ein Kommentar kein Befehl ist). Beachten Sie, dass dies völlig willkürlich ist und es keinen Grund gibt, warum diese Shells dies tun würden. yashund zshhaben diese Einschränkung nicht ( if false; then else echo x; fiund if false; then else fifunktionieren sogar gut mit ihnen).

Wie andere gesagt haben, können Sie einen noop-Befehl wie :(oder for nothing in; do nothing; done) verwenden oder die Logik mit dem !Schlüsselwort umkehren (verfügbar in POSIX-Shells, aber nicht in der Bourne-Shell (Sie werden feststellen, :dass dies in dieser Shell üblich war)). mkshund yashzufällig unterstützen if false; then () else echo x; fi(ich würde mich nicht darauf verlassen, da sich dies in zukünftigen Versionen ändern könnte).

Ein anderer Ansatz ist mit:

lsof... || {
  cmd1
  cmd2
}

Ein Unterschied ist jedoch der Gesamt-Exit-Status, der dem entspricht, lsofwenn ein lsofFehler auftritt.

Stéphane Chazelas
quelle
17
Dies ist zwar eine viel bessere Methode, um das zu tun, was @ Novice User versucht, aber es beantwortet die Frage überhaupt nicht.
SeeJayBee
Obwohl dies -execoft nützlich ist xargs, wird manchmal eine Shell-Schleife benötigt. In diesem Fall ist eine while read nameSchleife die bevorzugte Option (in bash mit GNU find können Sie die Option -0 für beide verwenden; möglicherweise müssen Sie auf newline verzichten).
Jan Hudec
@JanHudec, Es gibt tragbare Wege. -print0ist -exec printf '%s\0' {} +(aber portabel können Sie mit dieser Ausgabe nicht umgehen, außer wenn Sie dies berücksichtigen möchten perl), und mit find .//.und etwas Nachbearbeitung können Sie den Zeilenumbrüchen für entkommen xargs. Beachten Sie, dass es kein ist while read, es ist while IFS= read -r.
Stéphane Chazelas
@ Chris, ich habe der eigentlichen Frage eine Antwort hinzugefügt, da die Antwort akzeptiert wurde.
Stéphane Chazelas
91

Es scheint, dass Sie ein No-Op ausführen möchten, wenn die Datei geöffnet ist. Fügen Sie :daher einen Null-Befehl hinzu bash:

if lsof "$filename" > /dev/null; then
  # file is open
  :
else
  printf 'deleting %s\n' "$filename"
  rm -- "$filename"
fi

Wenn Sie nicht verwenden :, bashkönnen Sie den Code nicht analysieren und es wird ein Fehler wie angezeigt bash: syntax error near unexpected token 'else'.

cuonglm
quelle
Nie neu von :und es ist der erste Befehl, der in bash-builtins aufgeführt wird.
Bolov
26

Eine andere Alternative: kehren Sie Ihre Logik um.

if ! lsof "$filename" >/dev/null;then
    echo "deleting $filename"
    rm "$filename"
fi
Joseph R.
quelle
17

TL; DR

Keine der anderen Antworten beantwortet tatsächlich Ihre ursprüngliche Frage, warum der Befehl einen Syntaxfehler ausgibt. Dies wird durch einen fehlenden Befehl zwischen then und else verursacht .

Ein fehlender Befehl

Ihr ursprünglicher Code sieht folgendermaßen aus:

if lsof "$filename" > /dev/null
then
  # file is open
else
  echo "deleting $filename"
  rm "$filename"
fi

Das Problem ist, dass Sie zwischen then und else einen Kommentar haben , dieser jedoch nicht als Befehl behandelt wird. Kurz gesagt, Sie könnten das Problem (strukturell gesehen) wie folgt umschreiben:

$ if true; then else echo; fi
bash: syntax error near unexpected token `else'

Korrigieren Sie Ihre Syntax mit einem Bourne Builtin

Sie können dieses Problem beheben, indem Sie die tatsächlichen Befehle vor andere Befehle stellen. Ein Kommentar allein reicht jedoch nicht aus. Der If-Then- Abschnitt darf nicht leer sein. Wenn Sie einen Platzhalter möchten, können Sie den eingebauten Doppelpunkt verwenden . Beispielsweise:

$ if true; then :; else echo; fi

Durch einfaches Einfügen :in den Abschnitt zwischen then und else wird der aufgetretene Syntaxfehler behoben.

CodeGnome
quelle
1
Die Gnouc-Antwort, die auch die am häufigsten gestellte ist, befasst sich bereits mit der ursprünglichen Frage.
Juli
Antworte nur, um den Syntaxfehler zu beheben. FWIW, Sie können einen ähnlichen Fehler mit einem Semikolon am Anfang einer Zeile reproduzieren. Dies wird einen starken Hinweis geben. $ ; -bash: syntax error near unexpected token ';'
Matthew Hannigan