Qual é a diferença entre && e | no script bash?

12

Vamos pegar as duas linhas abaixo, que nos dão dois resultados diferentes.

p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ |  pwd) ; echo $p

Como os dois diferem?

Nam G VU
fonte
@heemayl Obrigado. Você pode extrair as diferenças no meu caso? Obrigado.
Nam G VU
1
Dica: Os comandos de ambos os lados da |execução em subshells.
heemayl

Respostas:

21

Em p=$(cd ~ && pwd):

  • A substituição do comando $(), é executada em um subshell

  • cd ~altera o diretório para ~(sua casa), se cdfor bem-sucedido ( &&), pwdimprime o nome do diretório em STDOUT; portanto, a sequência salva pserá o seu diretório inicial, por exemplo/home/foobar

Em p=$(cd ~ | pwd):

  • Novamente $()gera um subshell

  • Os comandos dos dois lados da |execução nos respectivos subshells (e ambos começam ao mesmo tempo)

  • isso cd ~é feito em um subshell e pwdem um subshell separado

  • para que você obtenha apenas o STDOUT, pwdou seja, de onde você executa o comando, este pode ser qualquer diretório que você possa imaginar; portanto p, conterá o nome do diretório de onde o comando é chamado, não o diretório inicial

heemail
fonte
Qual é o propósito do canal entre os comandos? cd ~não produz nenhuma saída e pwdnão lê nenhuma entrada.
Barmar
O segundo comando é essencialmente equivalente, (cd ~);p=$(pwd)não é?
Barmar
@ Barmar Sim, é o que a OP usou, e só estou explicando isso para ele.
amigos estão dizendo sobre heemayl
6

A questão principal é como os operadores &&e |conectam os dois comandos.

O &&conecta os comandos através do código de saída. O |conecta os dois comandos através dos descritores de arquivo (stdin, stdout).

Vamos simplificar primeiro. Podemos remover a tarefa e escrever:

echo $(cd ~ && pwd)
echo $(cd ~ | pwd)

Podemos até remover o sub-shell de execução de comando para analisar isso:

$ cd ~ && pwd
$ cd ~ | pwd

&&

Se mudarmos o prompt para mostrar o diretório em que os comandos são executados, algo como PS1='\w\$ ', veremos isso:

/tmp/user$ cd ~ && pwd
/home/user
~$ 
  • O comando cd ~alterou o "diretório atual" para a página inicial do usuário real que está executando o comando ( /home/user).
  • Como o resultado do comando foi bem-sucedido (código de saída 0), o próximo comando após o && é executado
  • E o "diretório de trabalho atual" é impresso.
  • O shell em execução mudou pwdpara ~como é mostrado pelo prompt de ~$.

Se a alteração do diretório não foi bem-sucedida (código de saída não 0) por algum motivo (o diretório não existe, as permissões bloqueiam a leitura do diretório), o próximo comando não será executado.

Exemplo:

/tmp/user$ false && pwd
/tmp/user$ _ 

O código de saída 1 de falseimpede a execução do próximo comando.

Portanto, o código de saída do "comando 1" é o que afeta o "comando 2".

Agora, os efeitos de todo o comando:

/tmp/user$ echo $(cd ~ && pwd)
/home/user
/tmp/user$ _

O diretório foi alterado, mas dentro de um sub shell $(…), o diretório alterado é impresso /home/user, mas é imediatamente descartado quando o sub shell é fechado. O pwd retorna para o diretório inicial ( /tmp/user).

|

Isto é o que acontece:

/tmp/user$ cd ~ | pwd
/tmp/user
/tmp/user$ _

O meta-caractere |(não um operador verdadeiro) sinaliza ao shell para criar o que é chamado de "Pipe", (no bash) cada comando em cada lado do pipe ( |) é definido dentro de cada sub-shell, primeiro o lado direito comando, então, o esquerdo. O descritor do arquivo de entrada ( /dev/stdin) do comando right é conectado ao descritor de saída ( /dev/stdout) e, em seguida, ambos os comandos são iniciados e deixados para interagir. O comando esquerdo ( cd -) não tem saída e, também, o comando direito ( pwd) não aceita entrada. Portanto, cada um roda independentemente dentro de cada subcasca.

  • O cd ~altera o pwd de um shell.
  • Ele pwdimprime a senha (completamente independente) da outra subcasca.

As alterações em cada invólucro são descartadas quando o tubo termina, o sub-invólucro externo não alterou a senha.

É por isso que os dois comandos são conectados apenas por "descritores de arquivo".
Nesse caso, não há nada enviado e nada lido.

Todo o comando:

$ echo "$(cd ~ | pwd)"

Apenas imprimirá o diretório em que o comando foi executado.

sorontar
fonte
5

Não tenho certeza se você quis dizer '|' ou '||' no seu segundo caso.

'|' em um shell canaliza a saída de um comando para a entrada de outro - um caso de uso comum é algo como: curl http://abcd.com/efgh | grep ijkl ou seja, execute um comando e use outro comando para processar a saída de um comando.

No exemplo que você fornece, é bastante absurdo, pois 'cd' normalmente não gera nenhuma saída e 'pwd' não espera nenhuma entrada.

'&&' e '||' são comandos de parceiros. Eles foram projetados para serem usados ​​da mesma maneira que os operadores lógicos "e" e "ou" na maioria dos idiomas. No entanto, as otimizações executadas fornecem um comportamento específico, que é um paradigma de programação de shell.

Para determinar o resultado de uma operação lógica "e", você só precisa avaliar a segunda condição se a primeira condição for bem-sucedida - se a primeira condição falhar, o resultado geral será sempre falso.

Para determinar o resultado de uma operação lógica "ou", você só precisa avaliar a segunda condição se a primeira condição falhar - se a primeira condição for bem-sucedida, o resultado geral será sempre verdadeiro.

Portanto, no shell, se você tiver, command1 && command2 command2será executado apenas quando command1tiver concluído e retornado um código de resultado bem-sucedido. Se você tiver command1 || command2 command2, será executado quando command1concluído, se command1retornar um código de falha.

Outro paradigma comum é ter command1um comando de teste - isso gera uma única linha if / then - por exemplo:

[ "$VAR" = "" ] && VAR="Value if empty"

É uma maneira (longa) de atribuir um valor a uma variável se ela estiver vazia no momento.

Existem muitos exemplos do uso desse processo em outro lugar no Stack Exchange

Michael Firth
fonte