Bevorzugte Syntax für zweizeilige lange Pipe

Bevorzugte Syntax für zweizeilige lange Pipe

Beim Schreiben einer langen Pipe ist es normalerweise übersichtlicher, sie auf zwei Zeilen aufzuteilen.

Diese lange Befehlszeile:

ruby -run -e httpd -- -p 5000 . 2>&1 | tee >(grep -Fq 'WEBrick::HTTPServer#start' && open localhost:5000) 

Könnte wie folgt unterteilt werden:

ruby -run -e httpd -- -p 5000 . 2>&1 \
   | tee >(grep -Fq 'WEBrick::HTTPServer#start' && open localhost:5000) 

Oder:

ruby -run -e httpd -- -p 5000 . 2>&1 |
    tee >(grep -Fq 'WEBrick::HTTPServer#start' && open localhost:5000) 

Zusamenfassend:

command1 \
    | command2

Oder:

command1 |
    command2

Mir ist bewusst, dass es sich hierbei möglicherweise um eine Stil- (Meinungs-)Frage handelt, aber: Gibt es eine bevorzugte Vorgehensweise und wenn ja, warum?

Antwort1

Fragen Sie sich, was das bewirken würde?

command1 \ 
   | command2

Ich kann den Unterschied nicht erkennen. Ich auch nicht, aber die Shell kann ihn erkennen. Schauen Sie genau hin, nach dem steht ein Leerzeichen \. Dies verhindert, dass die neue Zeile maskiert wird.

Verwenden Sie daher die andere Form, da diese sicherer ist. Hier mit demselben Fehler angezeigt ( |in diesem Fall ein Leerzeichen nach dem). Es verursacht jedoch keinen Fehler.

command1 | 
    command2

Antwort2

Ich bin mit den meisten Leuten hier nicht einverstanden; ich ziehe es immer vor,Vorein Verbindungsoperator wie beispielsweise eine Pipe:

command1 \
| command 2

(Sie müssen die zweite Zeile nicht einrücken; das Pipe-Zeichen selbst verbindet sie ganz offensichtlich mit der ersten.)

Dafür gibt es einige Gründe:

  • Es ist leichter zu sehender Verbinder; er geht nicht zwischen den Details der Zeile verloren. (Das ist besonders wichtig, wenn die Zeile lang ist und der Verbinder möglicherweise aus dem Blickfeld gescrollt oder zwischen den Zeilenumbrüchen verloren gegangen ist.) Wenn Sie Code schnell überfliegen, schauen Sie auf die linke Seite, denn dort befindet sich die Gesamtstruktur: in der Einrückung, den Klammern oder was auch immer eine bestimmte Sprache verwendet. Pipes und andere Verbinder sind für die Struktur wichtig, also sollten sie auch auf der linken Seite sein.

  • Es reiht sich einwenn Sie 3 oder mehr Leitungen umfassen. Auch hierdurch ist die Struktur der Pipeline auf einen Blick leicht zu erfassen.

  • Es ist näher an der Art, wie wir denken(Dies ist der subtilste und umstrittenste Punkt…) Wenn Sie eine Liste langsam vorlesen, damit jemand sie aufschreiben kann, sagen Sie „[Punkt 1]…(Pause)… und [Punkt 2]…(Pause)… und [Punkt 3].“; es würde sich unnatürlich anfühlen zu sagen „[Punkt 1] und…(Pause)… [Punkt 2] und…(Pause)… [Element 3].“ Das liegt daran, dass wir uns den Verbinder eher mit dem folgenden Element als mit dem vorherigen verbinden. (Sie können sich das Minuszeichen in der Arithmetik ähnlich vorstellen; es funktioniert wie eine Addition, verbindet sich aber enger mit der folgenden Zahl, indem es diese negiert.) Code ist leichter zu verstehen, wenn er unser Denken widerspiegelt.

Ich habe über die Jahre beide Möglichkeiten in vielen Sprachen ausprobiert und bin zu dem Schluss gekommen, dass es in den meisten Fällen wirklich hilft, Verbinder in die folgende Zeile einzufügen.

Antwort3

Damit es nicht so aussieht, als ob es niemandem gefallen würde:

command1 \
   | command2

Ich würde sagen, das tue ich.

Ich sehe das Problem mit den Leerzeichen am Ende, das durch Strg-Alt-Auslösen entsteht, als unproblematisch an. Editoren können davor warnen; Git warnt davor. Um das Ganze noch zu toppen, würde die Shell einen Syntaxfehler auslösen | command2, dem Benutzer die Datei- und Zeilennummer des Fehlers mitteilen und die Interpretation des Rests der Datei beenden:

$ cat f.sh
#!/bin/bash

echo foo \ 
| command2

echo bar
$ ./f.sh
foo  
./f.sh: line 4: syntax error near unexpected token `|'
./f.sh: line 4: `| command2'

Außerdem gibt es noch weitere Verwendungsmöglichkeiten für Zeilenfortsetzungs-Escapes. Beispielsweise, um einfache Befehle mit vielen Argumenten zu unterbrechen:

ffmpeg \
  -f x11grab \
  -video_size "$size" \
  -framerate "${framerate:-10}" \
  -i "${DISPLAY}${offset}" \
  -c:v ffvhuff \
  -f matroska \
  -

Sollten wir eine solche Verwendung auch vermeiden, weil wir uns nicht darauf verlassen können, dass wir nach dem Escape-Zeichen kein Leerzeichen einfügen?

Meine Präferenz ist rein eine Frage der Lesbarkeit und ziemlich subjektiv. Hier ist ein echtes Beispiel aus meiner Shell-Historie (mit Details, die durch foobar ersetzt wurden):

org-table-to-csv foobar.org \
| cq +H -q "
  select foo
    from t
    where bar = 'baz'
      and foo != ''" \
| sed -r 's/^|$/'\''/g' \
| sed -r ':b;$!{N;bb};s/\n/, /g'

Vergleichen mit:

org-table-to-csv foobar.org |
  cq +H -q "
    select foo
      from t
      where bar = 'baz'
        and foo != ''" |
  sed -r 's/^|$/'\''/g' |
  sed -r ':b;$!{N;bb};s/\n/, /g'

Hier ist ein anderes:

sed 's/ .*//' <<< "$blame_out"
| sort \
| uniq \
| tee >(sed "s/^/from pipe before grep filtering: /" > /dev/tty) \
| grep -vF "$(git show -s --format=%h "$from_commit")" \
| tee >(sed "s/^/from pipe before git show: /" > /dev/tty) \
| xargs git show -s --format='%cI %h' \
| tee >(sed "s/^/from pipe after git show: /" > /dev/tty) \
| sort -k1 \
| tail -1 \
| cut -d' ' -f2

Vergleichen mit:

sed 's/ .*//' <<< "$blame_out"
  sort |
  uniq |
  tee >(sed "s/^/from pipe before grep filtering: /" > /dev/tty) |
  grep -vF "$(git show -s --format=%h "$from_commit")" |
  tee >(sed "s/^/from pipe before git show: /" > /dev/tty) |
  xargs git show -s --format='%cI %h' |
  tee >(sed "s/^/from pipe after git show: /" > /dev/tty) |
  sort -k1 |
  tail -1 |
  cut -d' ' -f2

Antwort4

Ich dachte, die Antwort darauf wäre einfach, aber ich sehe, dass @JoL und @gidds nicht meiner Meinung sind.

My brain prefers reading a line and not having to scan the next line \
:

  foo bar baz ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... \

In the above I will have to see \
, what is on line 2 \
, before I can tell \
, what the command does \
. Maybe the command is complete \
? Or maybe the command continues \
  on the next line \
?

To me it is much easier to read,
if \ is only used,
when a command cannot fit on a line.

Beim Durchlesen meines Codes sehe ich auch Kommentare als Problem:

foo ... ... ... ... ... ... ... ... |
    # Now this does bar
    bar ... ... ... ... ... ... ... ... ||
    # And if that fails: fubar
    fubar

Ich bin mir nicht sicher, wie Sie Kommentare in der Mitte einer Pipeline einfügen können, wenn Sie \+ newline vor |oder ||oder verwenden &&. Wenn das nicht möglich ist, ist das meiner Meinung nach das wichtigste Problem. Code ist ohne Kommentare nicht wartbar, und Kommentare sollten normalerweise so nah wie möglich am Code stehen, um die Aktualisierung der Dokumentation zu fördern, wenn Sie den Code ändern.

Emacs führt die Einrückung automatisch für mich durch, sodass die Einrückung nicht einmal eine zusätzliche Belastung darstellt:

# This is indented automatically in emacs
ruby -run -e httpd -- -p 5000 . 2>&1 |
    # Send the output to the screen and to grep
    tee >(grep -Fq 'WEBrick::HTTPServer#start' &&
              # If grep matches, open localhost:5000
              open localhost:5000) 
# Here is where emacs indents the next command to

verwandte Informationen