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?

    
por Nam G VU 14.11.2016 / 06:23

3 respostas

21

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

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

  • cd ~ altera o diretório para ~ (sua página inicial), se cd for bem-sucedido ( && ), então pwd imprime o nome do diretório em STDOUT, portanto, a sequência salva em p seja o seu diretório inicial, por exemplo /home/foobar

Em p=$(cd ~ | pwd) :

  • Novamente $() gera uma subshell

  • Os comandos em ambos os lados de | são executados em subshells respectivos (e ambos começam ao mesmo tempo)

  • então cd ~ é feito em uma subshell e pwd em uma subshell separada

  • para que você obtenha apenas o STDOUT de pwd ie de onde você executa o comando, este pode ser qualquer diretório, como você pode imaginar, portanto p conterá o nome do diretório de onde o comando é invocado , não é o seu diretório pessoal

por 14.11.2016 / 06:40
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: %código% isto é, execute um comando e use outro comando para processar a saída de um comando.

No exemplo que você dá, é bastante sem sentido, já que 'cd' normalmente não gera saída, e 'pwd' não espera nenhuma entrada.

'& &' e '||' são comandos de parceiros embora. Eles são projetados para serem usados da mesma maneira que os operadores lógicos "e" e "ou" na maioria dos idiomas. No entanto, as otimizações que são realizadas dão a elas 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 curl http://abcd.com/efgh | grep ijkl command1 && command2 será executado somente quando command2 tiver concluído e retornado um código de resultado bem-sucedido. Se você tiver command1 command1 || command2 será executado quando command2 for concluído se command1 retornar um código de falha.

Outro paradigma comum é ter command1 seja um comando de teste - isso gera uma única linha se / então declaração - por exemplo:

command1

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

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

    
por 14.11.2016 / 14:17
5

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

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

Vamos simplificar primeiro. Podemos remover a tarefa e escrever:

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

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

$ cd ~ && pwd
$ cd ~ | pwd

& &

Se alterarmos o prompt para mostrar o diretório onde os comandos são executados, algo como PS1='\w\$ ' , veremos isto:

/tmp/user$ cd ~ && pwd
/home/user
~$ 
  • O comando cd ~ alterou o "diretório atual" para a home 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 alterou seu pwd para ~ , conforme mostrado no prompt de ~$ .

Se a mudança de diretório foi mal sucedida (código de saída não 0) por algum motivo (diretório não existe, bloco de permissões lendo o 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 false impede a execução do próximo comando.

Assim, 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 em /home/user , mas é imediatamente descartado quando o sub-shell é fechado. O pwd volta a ser o diretório inicial ( /tmp/user ).

|

Isso é o que acontece:

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

O meta-caractere | (não um verdadeiro operador) sinaliza ao shell para criar o que é chamado de "Pipe", (no bash) cada comando em cada lado do pipe ( | ) é definido dentro de cada próprio sub-shell, primeiro o comando do lado direito e, em seguida, o da esquerda. O descritor do arquivo de entrada ( /dev/stdin ) do comando à direita está 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 correto ( pwd ) não aceita entrada. Então, cada um é executado independentemente dentro de cada sub-shell.

  • O cd ~ altera o pwd de um shell.
  • O pwd imprime o pwd (completamente independente) do outro sub-shell.

As alterações em cada shell são descartadas quando o pipe termina, o sub-shell externo não alterou o pwd.

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

O comando inteiro:

$ echo "$(cd ~ | pwd)"

Apenas imprimirá o diretório onde o comando foi executado.

    
por 14.11.2016 / 19:36

Tags