当编写长管道时,将其分成两行通常会更清晰。
这么长的命令行:
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