兩行長管道的首選語法

兩行長管道的首選語法

當編寫長管道時,將其分成兩行通常會更清晰。

這麼長的命令列:

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

可分為:

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

或者:

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

簡而言之:

command1 \
    | command2

或者:

command1 |
    command2

我意識到這可能是風格(意見)問題,但是:是否有首選方法,如果有,為什麼?

答案1

問問自己這會做什麼?

command1 \ 
   | command2

看不出差別。我也不能,但 shell 可以。仔細一看,後面有一個空格\。這會阻止換行符號被轉義。

因此,請使用其他形式,因為它更安全。此處顯示相同的錯誤(|在本例中,後面有一個空格)。但它不會導致錯誤。

command1 | 
    command2

答案2

我不同意這裡大多數人的觀點;我總是更喜歡包裹連接運算符,例如管道:

command1 \
| command 2

(您不需要縮排第二行;管道本身將其非常明顯地連結到第一行。)

造成這種情況的原因有以下幾個:

  • 更容易看到木匠;它不會迷失在線條的細節中。 (如果行很長,並且連接符號可能會滾動到看不見的地方,或者在換行中丟失,這一點尤其重要。)當您快速掃描程式碼時,您會向下看左側,因為那是整體結構的所在是:在縮排、大括號或任何特定語言所使用的內容。 管道和其他連接件對結構很重要,因此它們也應該位於左側。

  • 它排隊如果您跨越 3 條或更多條線。同樣,這使得管道的結構一目了然。

  • 更接近我們的想法。 (這是最微妙和最有爭議的一點...)如果你正在慢慢地閱讀一個列表,以便有人可以把它寫下來,你會說「[項目1]...(暫停)…以及[項目 2]…(暫停)……以及[第 3 項]。說「[項目 1] 和…」會感覺不自然。(暫停)…[項目 2] 和…(暫停)……[第 3 項]。這是因為我們認為連接器比前一個項目更多地附加到後一個項目。 (您可以用類似的術語來思考算術中的減號;它的工作原理類似於加法,但通過否定它與後面的數字聯繫得更緊密。)當代碼反映了我們的想法時,它就更容易理解。

多年來,我在多種語言中嘗試過這兩種方法,並且發現在大多數情況下將連接符放在下一行確實有幫助。

答案3

好吧,只是為了避免看起來沒人願意:

command1 \
   | command2

我會說我願意。

我認為 ctrl-alt-delor 引發的尾隨空格問題不是問題。編輯可以對此發出警告; git 對此發出警告。最重要的是,shell 會在 上引發語法錯誤| command2,為使用者提供錯誤的檔案和行號,並停止解釋檔案的其餘部分:

$ 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'

還有一個事實是,行連續轉義有更多用途。例如,要破壞具有許多參數的簡單命令:

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

我們是否也應該避免這種用法,因為我們不能相信自己不會在轉義之後加上空格?

我的偏好純粹是為了可讀性,而且相當主觀。這是我的 shell 歷史記錄中的一個真實範例(詳細資訊以 foobar 取代):

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'

相較於:

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'

這是另一個:

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

相較於:

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

答案4

我認為這個問題的答案很簡單,但我可以看到@JoL 和@gidds 不同意我的觀點。

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.

閱讀我的程式碼,我還發現註解是一個問題:

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

我不確定如果您在or或\之前使用 + 換行符,您將如何在管道中間進行註釋。如果這不可能,我認為這是最重要的問題。如果沒有註釋,程式碼就無法維護,註釋通常應盡可能接近程式碼,以鼓勵在更改程式碼時更新文件。|||&&

Emacs 會自動為我進行縮排,因此縮排甚至不是額外的負擔:

# 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

相關內容