两行长管道的首选语法

两行长管道的首选语法

当编写长管道时,将其分成两行通常会更清晰。

这么长的命令行:

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

相关内容