
При написании длинной трубы обычно удобнее разделить ее на две строки.
Эта длинная командная строка:
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
Не вижу разницы. Я тоже не вижу, но оболочка видит. Посмотрите внимательно, после \
. Это не дает экранировать новую строку.
Поэтому используйте другую форму, так как она безопаснее. Здесь показано с той же ошибкой (в данном случае пробел после |
). Но это не вызывает ошибку.
command1 |
command2
решение2
Я не соглашусь с большинством людей здесь; я всегда предпочитаю оборачиватьдооператор соединения, такой как труба:
command1 \
| command 2
(Вам не нужно делать отступ во второй строке; вертикальная черта сама по себе очень очевидно связывает ее с первой.)
На это есть несколько причин:
Это легче увидеть.соединитель; он не теряется среди деталей строки. (Это особенно важно, если строка длинная, и соединитель мог прокрутиться или потеряться среди переносов строк.) Когда вы быстро просматриваете код, вы смотрите вниз на левую сторону, потому что именно там находится общая структура: в отступах, фигурных скобках или в том, что используется в конкретном языке. Каналы и другие соединительные элементы важны для структуры, поэтому они тоже должны быть слева.
Он выстраивается в линиюесли вы охватываете 3 или более линий. Опять же, это позволяет легко охватить структуру конвейера с первого взгляда.
Это ближе к нашему образу мышления.. (Это самый тонкий и спорный момент…) Если вы медленно зачитываете список, чтобы кто-то мог его записать, вы бы сказали: «[Пункт 1]…(Пауза)… и [Пункт 2]…(Пауза)… и [Элемент 3]»; было бы неестественно сказать «[Элемент 1] и…(Пауза)… [Пункт 2] и…(Пауза)… [Элемент 3]». Это потому, что мы думаем, что соединитель присоединяется к следующему элементу больше, чем к предыдущему. (Вы можете думать о знаке минус в арифметике в похожих терминах; он работает как сложение, но более тесно соединяется со следующим числом, отрицая его.) Код легче понимать, когда он отражает наши мысли.
За эти годы я попробовал оба способа во многих языках и обнаружил, что размещение соединителей в следующей строке действительно помогает в большинстве случаев.
решение3
Ну, просто чтобы не создать впечатление, что это никому не нравится:
command1 \
| command2
Я скажу, что да.
Я считаю, что проблема с конечными пробелами, вызванная ctrl-alt-delor, не является проблемой. Редакторы могут предупреждать об этом; git предупреждает об этом. В довершение всего, оболочка выдаст синтаксическую ошибку на | 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 \
-
Должны ли мы избегать такого использования, потому что мы не можем себе позволить не ставить пробел после символа перехода?
Мои предпочтения — это исключительно вопрос читабельности, и они довольно субъективны. Вот реальный пример из моей истории оболочки (с заменой деталей на 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
Я не уверен, как вы вообще будете делать комментарии в середине конвейера, если вы используете \
+ newline перед |
или ||
или &&
. Если это невозможно, я думаю, что это самая важная проблема. Код не может быть поддержан без комментариев, и комментарии обычно должны быть как можно ближе к коду, чтобы поощрять обновление документации при изменении кода.
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