Sintaxe preferida para tubo longo de duas linhas

Sintaxe preferida para tubo longo de duas linhas

Ao escrever um tubo longo, geralmente é mais claro separá-lo em duas linhas.

Esta longa linha de comando:

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

Poderia ser dividido como:

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

Ou:

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

Resumidamente:

command1 \
    | command2

Ou:

command1 |
    command2

Sei que esta pode ser uma questão de estilo (opinião), mas: existe uma forma preferida e, em caso afirmativo, por quê?

Responder1

Pergunte a si mesmo o que isso faria?

command1 \ 
   | command2

Não consigo ver a diferença. Nem eu posso, mas a casca pode. Olhe atentamente, há um espaço após o \. Isso impede que a nova linha seja escapada.

Portanto use a outra forma, pois é mais segura. Mostrado aqui com o mesmo erro (um espaço após |neste caso). Mas isso não causa um bug.

command1 | 
    command2

Responder2

Vou discordar da maioria das pessoas aqui; Eu sempre prefiro embrulharantesum operador de união, como um tubo:

command1 \
| command 2

(Você não precisa recuar a segunda linha; o próprio tubo a vincula obviamente à primeira.)

Existem algumas razões para isso:

  • É mais fácil vero marceneiro; não se perde entre os detalhes da linha. (Isso é especialmente importante se a linha for longa e o marceneiro puder ter sido rolado para fora da vista ou perdido na quebra de linha.) Ao digitalizar o código rapidamente, você olha para o lado esquerdo, porque é onde está a estrutura geral. é: no recuo, nas chaves ou no que quer que um idioma específico use. Tubos e outras juntas são importantes para a estrutura, por isso também devem ficar à esquerda.

  • Ele se alinhase você estiver abrangendo 3 ou mais linhas. Novamente, isso torna a estrutura do pipeline fácil de entender rapidamente.

  • Está mais próximo da maneira como pensamos. (Este é o ponto mais sutil e controverso…) Se você estiver lendo uma lista lentamente, para que alguém possa anotá-la, você diria “[Item 1]…(pausa)… e [Item 2]…(pausa)… e [Item 3].”; não seria natural dizer “[Item 1] e…(pausa)… [Item 2] e…(pausa)… [Item 3].” Isso porque pensamos que o marceneiro está apegado ao item seguinte mais do que ao anterior. (Você pode pensar no sinal de menos na aritmética em termos semelhantes; ele funciona como uma adição, mas se conecta mais estreitamente ao número seguinte, negando-o.) O código é mais fácil de seguir quando reflete nosso pensamento.

Eu tentei as duas maneiras em vários idiomas ao longo dos anos e descobri que colocar joiners na linha a seguir realmente ajuda na maioria dos casos.

Responder3

Bem, só para evitar que pareça que ninguém preferiria:

command1 \
   | command2

Eu vou dizer que sim.

Vejo o problema de espaço à direita levantado por ctrl-alt-delor como um problema. Os editores podem alertar sobre isso; git avisa sobre isso. Para completar, o shell geraria um erro de sintaxe on | command2, fornecendo ao usuário o arquivo e o número da linha do erro e pararia de interpretar o restante do arquivo:

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

Há também o fato de que há mais usos para escapes de continuação de linha. Por exemplo, para quebrar comandos simples que possuem muitos argumentos:

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

Deveríamos evitar esse uso também porque não podemos confiar em nós mesmos para não colocarmos um espaço após a fuga?

Minha preferência é puramente uma questão de legibilidade e bastante subjetiva. Aqui está um exemplo real do meu histórico de shell (com detalhes substituídos por 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'

Comparado a:

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'

Aqui está outro:

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

Comparado a:

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

Responder4

Achei que a resposta era fácil, mas vejo que @JoL e @gidds discordam de mim.

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.

Lendo meu código, também vejo os comentários como um problema:

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

Não tenho certeza de como você faria comentários no meio de um pipeline se usasse \+ nova linha antes de |or ||or &&. Se isso não for possível, penso que este é o problema mais importante. O código não pode ser mantido sem comentários, e os comentários normalmente devem estar o mais próximo possível do código para incentivar a atualização da documentação quando você altera o código.

O Emacs faz o recuo automaticamente para mim, então o recuo não é nem um fardo extra:

# 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

informação relacionada