Diferença de using () e $ () para executar uma série de comandos

6

Atualmente, estou tentando criar um script que crie bytes que serão canalizados como entrada para o netcat.

Aqui está a ideia do script:

(perl -e "print \"$BYTES\x00\";

cat file;

perl -e "print \"More bytes\"x16 . \"\r\n\"";) | netcat ip port

Eu tentei usar ambos usando um subshell e substituição de comandos (ex. com $ ()) para executar os comandos. No entanto, não consigo entender por que a saída do script ao usar a substituição de comando está errada. Eu suspeito que a substituição de comando canaliza incorretamente sua saída ao executar vários comandos. Alguém pode me explicar por que isso é assim?

EDITAR

Aqui está a variante que usou a substituição de comando:

$(perl -e "print \"$BYTES\x00\";

cat file;

perl -e "print \"More bytes\"x16 . \"\r\n\"";) | netcat ip port
    
por MykelXIII 02.07.2015 / 15:04

2 respostas

12

Ok, vamos quebrar isso. Um subshell executa seu conteúdo em uma cadeia (isto é, agrupa-os). Isso realmente faz sentido intuitivo como um subshell é criado simplesmente cercando a cadeia de comandos com () . Mas, além do conteúdo do subshell ser agrupado em execução, você ainda pode usar um subshell como se fosse um único comando. Ou seja, um subshell ainda tem um stdin , stdout e stderr , então você pode canalizar coisas de e para um subshell.

Por outro lado, a substituição de comando não é a mesma coisa que simplesmente encadear comandos juntos. Em vez disso, a substituição de comando serve para atuar um pouco como um acesso variável, mas com uma chamada de função. As variáveis, ao contrário dos comandos, não possuem os descritores de arquivos padrão, portanto você não pode canalizar nada para ou de uma variável (geralmente falando), e o mesmo vale para as substituições de comandos.

Para tentar deixar isso mais claro, o que segue é um conjunto de exemplos talvez pouco claros (mas precisos) e um conjunto de, o que eu acho que pode ser, exemplos mais fáceis de entender.

Digamos que o comando date -u forneça o seguinte:

Thu Jul  2 13:42:27 UTC 2015

Mas, queremos manipular a saída deste comando. Então, vamos canalizá-lo para algo como sed :

$ date -u | sed -e 's/ /    /g'
Thu    Jul        2    13:42:27    UTC    2015

Uau, isso foi divertido! O seguinte é completamente equivalente ao acima (exceto algumas diferenças de ambiente que você pode ler nas páginas man sobre seu shell):

$ (date -u) | sed -e 's/ /    /g'
Thu    Jul        2    13:42:27    UTC    2015

Isso não deve ser surpresa, já que tudo que fizemos foi o grupo date -u . No entanto, se fizermos o seguinte, obteremos algo que pode parecer um pouco estranho no início:

$ $(date -u) | sed -e 's/ /    /g'
command not found: Thu

Isso ocorre porque $(date -u) equivale a digitar exatamente o que date -u gera. Então, o acima é equivalente ao seguinte:

$ Thu Jul  2 13:42:27 UTC 2015 | sed -e 's/ /    /g'

O que, naturalmente, irá cometer erros porque Thu não é um comando (pelo menos não um que eu saiba); e certamente não canaliza nada para stdout (então sed nunca receberá nenhuma entrada).

Mas, como sabemos que as substituições de comandos funcionam como variáveis, podemos corrigir esse problema facilmente porque sabemos como canalizar o valor de uma variável para outro comando:

$ echo $(date -u) | sed -e 's/ /    /g'
Thu    Jul        2    13:42:27    UTC    2015

Mas, como qualquer variável no bash , você provavelmente deve citar as substituições de comandos com "" .

Agora, para o exemplo talvez mais simples; considere o seguinte:

$ pwd
/home/hypothetical
$ echo pwd
pwd
$ echo "$(pwd)"
/home/hypothetical
$ echo "$HOME"
/home/hypothetical
$ echo (pwd)
error: your shell will tell you something weird that roughly means “Whoa! you tried to have me echo something that isn't text!”
$ (pwd)
/home/hypothetical

Não sei como descrevê-lo mais simples que isso. A substituição de comandos funciona como um acesso variável, onde o subshell ainda funciona como um comando.

    
por 02.07.2015 / 16:04
3

subshell

  • (command) executará o comando em subshell, isso é útil, se você tiver mais de um comando.

    • (ls) | wc canalizará ls para wc , obviamente você pode escrever ls | wc
    • (ls ; date) | wc canalizará o resultado de ls e date para wc . usar ls ; date | wc resultará em apenas date sendo canalizado para wc .

substituição

  • $(command) executará o comando e substituirá pela saída. por exemplo,

    echo $(date)
    

substituirá $(date) por Thu Jul 2 15:20:43 CEST 2015 e, em seguida,

    echo Thu Jul  2 15:20:43 CEST 2015

reunindo

você pode combinar os dois.

file=/hello/word
( printf "%s has %d bytes\n" ${file} $(wc -c < $file) ; date ) | netcat ... 
  • você pode usar $file ou ${file}
por 02.07.2015 / 15:27