¿Cuál es la diferencia entre && y | en script bash?

13

Tomemos las dos líneas a continuación que nos dan dos resultados diferentes.

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

¿Cómo difieren los dos?

Nam G VU
fuente
@heemayl Gracias. ¿Puedes extraer las diferencias en mi caso? Gracias.
Nam G VU
1
Sugerencia: los comandos a cada lado de la |ejecución en subcapas.
heemayl

Respuestas:

21

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

  • La sustitución del comando $(), se ejecuta en una subshell

  • cd ~cambia el directorio a ~(su inicio), si cdtiene éxito ( &&), pwdimprime el nombre del directorio en STDOUT, por lo tanto, la cadena guardada pserá su directorio de inicio, por ejemplo/home/foobar

En p=$(cd ~ | pwd):

  • Nuevamente $()genera una subshell

  • Los comandos en ambos lados de la |ejecución en subcapas respectivas (y ambos comienzan al mismo tiempo)

  • así cd ~se hace en una subshell y pwden una subshell separada

  • para que solo obtenga el STDOUT, pwdes decir, desde donde ejecuta el comando, este puede ser cualquier directorio que pueda imaginar, por plo tanto , contendrá el nombre del directorio desde donde se invoca el comando, no su directorio de inicio

heemayl
fuente
¿Cuál es el propósito de la tubería entre los comandos? cd ~no produce ninguna salida y pwdno lee ninguna entrada.
Barmar
El segundo comando es esencialmente equivalente a (cd ~);p=$(pwd)¿no es así?
Barmar
@Barmar Sí, es lo que OP ha usado, y solo se lo estoy explicando.
heemayl
7

El tema central es cómo los operadores &&y |conectan los dos comandos.

El &&conecta los comandos a través del código de salida. El |conecta los dos comandos a través de los descriptores de archivo (stdin, stdout).

Vamos a simplificar primero. Podemos eliminar la tarea y escribir:

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

Incluso podemos eliminar el sub-shell de ejecución de comandos para analizar esto:

$ cd ~ && pwd
$ cd ~ | pwd

&&

Si cambiamos la solicitud para mostrar el directorio donde se ejecutan los comandos, algo así PS1='\w\$ ', veremos esto:

/tmp/user$ cd ~ && pwd
/home/user
~$ 
  • El comando cd ~cambió el "directorio actual" a la página de inicio del usuario real que está ejecutando el comando ( /home/user).
  • Como el resultado del comando fue exitoso (código de salida 0), se ejecuta el siguiente comando después de &&
  • Y se imprime el "directorio de trabajo actual".
  • El shell en ejecución ha cambiado pwda ~como se muestra en la solicitud de ~$.

Si el cambio de directorio no tuvo éxito (el código de salida no es 0) por alguna razón (el directorio no existe, los permisos bloquean la lectura del directorio), el siguiente comando no se ejecutará.

Ejemplo:

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

El código de salida de 1 falseimpide la ejecución del siguiente comando.

Por lo tanto, el código de salida del "comando 1" es lo que afecta al "comando 2".

Ahora, los efectos de todo el comando:

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

El directorio se modificó, pero dentro de un sub-shell $(…), el directorio modificado se imprime /home/user, pero se descarta inmediatamente cuando el sub-shell se cierra. El pwd vuelve a ser el directorio inicial ( /tmp/user).

El |

Esto es lo que pasa:

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

El meta-carácter |(no es un operador verdadero) le indica al shell que cree lo que se llama un "Pipe", (en bash) cada comando en cada lado del pipe ( |) se configura dentro de cada sub-shell, primero el lado derecho comando, entonces, el izquierdo. El descriptor del archivo de entrada ( /dev/stdin) del comando derecho se conecta al descriptor de salida ( /dev/stdout) y luego ambos comandos se inician y se dejan interactuar. El comando izquierdo ( cd -) no tiene salida y, además, el comando derecho ( pwd) no acepta entrada. Por lo tanto, cada uno se ejecuta de forma independiente dentro de cada sub-shell.

  • El cd ~cambia el pwd de un shell.
  • El pwdimprime el pwd (completamente independiente) de la otra sub-shell.

Los cambios en cada caparazón se descartan cuando termina la tubería, la subcapa externa no ha cambiado la pwd.

Es por eso que los dos comandos están conectados solo por "descriptores de archivo".
En este caso, no se envía nada y no se lee nada.

Todo el comando:

$ echo "$(cd ~ | pwd)"

Solo imprimirá el directorio donde se ejecutó el comando.

sorontar
fuente
5

No estoy seguro si quisiste decir '|' o '||' en tu segundo caso.

'|' en un shell canaliza la salida de un comando a la entrada de otro; un caso de uso común es algo como: curl http://abcd.com/efgh | grep ijkl es decir, ejecutar un comando y usar otro comando para procesar la salida de un comando.

En el ejemplo que da, es bastante absurdo, ya que 'cd' generalmente no genera ninguna salida y 'pwd' no espera ninguna entrada.

'&&' y '||' son comandos de pareja sin embargo. Están diseñados para usarse de la misma manera que los operadores lógicos "y" y "o" en la mayoría de los idiomas. Sin embargo, las optimizaciones que se realizan les dan un comportamiento específico que es un paradigma de programación de shell.

Para determinar el resultado de una operación lógica "y", solo necesita evaluar la segunda condición si la primera condición tiene éxito; si la primera condición falla, el resultado general siempre será falso.

Para determinar el resultado de una operación lógica "u", solo necesita evaluar la segunda condición si la primera falla; si la primera condición tiene éxito, el resultado general siempre será verdadero.

Entonces, en el shell, si command1 && command2 command2solo se ejecutará cuando se command1haya completado y devuelto un código de resultado exitoso. Si lo tiene command1 || command2 command2, se ejecutará cuando se command1complete si command1devuelve un código de falla.

Otro paradigma común es tener command1un comando de prueba; esto genera una sola línea si / luego declaración - por ejemplo:

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

Es una forma (de largo aliento) de asignar un valor a una variable si actualmente está vacía.

Hay muchos ejemplos del uso de este proceso en otras partes de Stack Exchange

Michael Firth
fuente