Wie kann ich einen Shell-Befehl bei Verwendung einer IF-Anweisung auf mehrere Zeilen aufteilen?

384

Wie kann ich einen Befehl über mehrere Zeilen in der Shell aufteilen, wenn der Befehl Teil einer ifAnweisung ist?

Das funktioniert:

if ! fab --fabfile=.deploy/fabfile.py --forward-agent --disable-known-hosts deploy:$target; then rc=1                                                                       
fi

Das funktioniert nicht:

# does not work:
if ! fab --fabfile=.deploy/fabfile.py \ 
  --forward-agent \
  --disable-known-hosts deploy:$target; then   
  rc=1
fi

Anstatt den gesamten Befehl auszuführen, erhalte ich:

./script.sh: line 73: --forward-agent: command not found

Noch wichtiger ist, was fehlt in meinem Verständnis von Bash, das mir helfen wird, dieses und ähnliche Probleme in Zukunft zu verstehen?

Dmitry Minkovsky
quelle
2
Was ist der Fehler? Ich kann $ if ! cp -n log/server1.log \ > .; then echo no copy; fiohne Fehler ausführen , mit einem Zeilenumbruch nach\
Miserable Variable
15
Haben Sie Leerzeichen nach den Backslashes des Terminals \ ? Sie sind ziemlich schwer zu sehen. In diesem Fall möchten Sie möglicherweise prüfen, ob Sie Ihren Editor dazu bringen können, nachgestellte Leerzeichen zu entfernen oder sie besser sichtbar zu machen.
Msw
10
Ja, es waren Leerzeichen nach den Backslashes des Terminals. Total. Vielen Dank.
Dmitry Minkovsky
Und ja, sorry, ich hätte den "Fehler" posten sollen (unerwartetes Ergebnis)! Mein Fehler! Jetzt bearbeiten.
Dmitry Minkovsky
Was war dein Verständnis? Es ist auch nicht Teil der Frage.
hakre

Antworten:

566

Die Zeilenfortsetzung schlägt fehl, wenn Sie nach dem Backslash und vor der neuen Zeile Leerzeichen (Leerzeichen oder Tabulatorzeichen) haben. Ohne solche Leerzeichen funktioniert Ihr Beispiel gut für mich:

$ cat test.sh
if ! fab --fabfile=.deploy/fabfile.py \
   --forward-agent \
   --disable-known-hosts deploy:$target; then
     echo failed
else
     echo succeeded
fi

$ alias fab=true; . ./test.sh
succeeded
$ alias fab=false; . ./test.sh
failed

Einige Details, die aus den Kommentaren hervorgehen: Der Zeilenumbruch in der Shell ist kein Sonderfall. Es ist lediglich ein Beispiel für die allgemeine Regel, dass ein Backslash das unmittelbar folgende Zeichen "zitiert" und so jede Sonderbehandlung verhindert, der es normalerweise unterliegen würde. In diesem Fall ist das nächste Zeichen eine neue Zeile, und die spezielle Behandlung, die verhindert wird, beendet den Befehl. Normalerweise wird ein zitiertes Zeichen buchstäblich in den Befehl aufgenommen. Ein umgekehrter Zeilenumbruch wird stattdessen vollständig gelöscht. Ansonsten ist der Mechanismus der gleiche. Und der Backslash zitiert nur das unmittelbar folgende Zeichen; Wenn es sich bei diesem Zeichen um ein Leerzeichen oder eine Registerkarte handelt, wird nur ein Leerzeichen oder eine Registerkarte in Anführungszeichen gesetzt, und alle nachfolgenden Zeilenumbrüche bleiben ohne Anführungszeichen.

Mark Reed
quelle
5
Mark, weißt du, ich muss Leerzeichen gehabt haben. Ich kann den Fehler nur reproduzieren, wenn ich nach dem `s. For example, when adding one after the first ` Leerzeichen Leerzeichen hinzufüge ./soundops: line 73: --forward-agent: command not found. Mein Problem war, dass ich diesen Fehler nicht verstanden habe. Warum führt ein Leerzeichen zu diesem Fehler? Das Leerzeichen + \n"negiert" das `` und begrenzt einen Befehl?
Dmitry Minkovsky
83
Ein Backslash vor der Newline verhindert, dass die Newline den Befehl beendet. Aber genau wie spezielle Escape-Sequenzen wie "\ n" nur mit nichts zwischen dem Backslash und dem n funktionieren, funktioniert Backslash-Newline nur mit nichts zwischen dem Backslash und dem Newline.
Mark Reed
18
Hahaha wow, das macht natürlich Sinn. Ich habe es nie so gesehen. Augenöffnend und doch so einfach: Es ist nur eine entkommene Newline. Ich hasse unsichtbare Charaktere. Sie würden für mich so viel sinnvoller sein, wenn sie alle nur sichtbar wären. Vielen Dank!
Dmitry Minkovsky
7
In den meisten Editoren können Sie diese unsichtbaren Zeichen sichtbar machen.
Lucasvc
1
Der Backslash und die Newline werden aus der effektiven Befehlszeile gelöscht, aber alle führenden Leerzeichen in der nächsten Zeile bleiben erhalten. Ob es sich also um ein Problem handelt oder nicht, hängt davon ab, ob Leerzeichen an diesem Punkt des einzeiligen Befehls ein Problem darstellen.
Mark Reed
52

Für Benutzer von Windows / WSL / Cygwin usw.:

Stellen Sie sicher, dass Ihre Zeilenenden Standard-Unix-Zeilenvorschübe sind, dh nur \n(LF).

Bei Verwendung von \r\nCRLF-Zeilenenden ( Windows Line Endings ) wird der Befehlszeilenumbruch unterbrochen.


Dies liegt daran, dass \das Ende einer Zeile mit dem Windows-Zeilenende in übersetzt wird \ \r \n.
Wie Mark oben richtig erklärt:

Die Zeilenfortsetzung schlägt fehl, wenn Sie nach dem Backslash und vor der neuen Zeile Leerzeichen haben.

Dies umfasst nicht nur Leerzeichen ( ) oder Tabulatoren ( \t), sondern auch den Wagenrücklauf ( \r).

Tschechologie
quelle
1
Dies behebt das Problem, das beim Erstellen eines Skripts in Windows und bei der Verwendung in Windows bash (z. B. bash -c MyShellScript.sh, wo MyShellScript.sh im Windows-Editor erstellt wurde) erstellt wurde. Sie müssen MyShellScript.sh im UNIX-Format speichern, möglicherweise mit Notepad ++.
BSalita