¿Cuáles son los operadores de control y redirección del shell?

¿Cuáles son los operadores de control y redirección del shell?

A menudo veo tutoriales en línea que conectan varios comandos con diferentes símbolos. Por ejemplo:

command1 |  command2
command1 &  command2
command1 || command2    
command1 && command2

Otros parecen estar conectando comandos a archivos:

command1  > file1
command1  >> file1

¿Qué son estas cosas? ¿Cómo se llaman? ¿Qué hacen? ¿Hay más de ellos?


Meta hilo sobre esta pregunta..

Respuesta1

Estos se llaman operadores de shell y sí, hay más. Daré una breve descripción de los más comunes entre las dos clases principales,operadores de controlyoperadores de redireccióny cómo funcionan con respecto al shell bash.

A. Operadores de control

Definición POSIX

En el lenguaje de comandos del shell, un token que realiza una función de control.
Es uno de los siguientes símbolos:

&   &&   (   )   ;   ;;   <newline>   |   ||

Y |&en fiesta.

una !esnoun operador de control sino unPalabra reservada. Se convierte en un NO lógico [operador de negación] dentroExpresiones aritméticasy dentro de las construcciones de prueba (sin dejar de requerir un delimitador de espacio).

A.1 Terminadores de lista

  • ;: Ejecutará un comando tras finalizar otro, independientemente del resultado del primero.

      command1 ; command2
    

Primero command1se ejecuta, en primer plano, y una vez finalizado command2se ejecutará.

Una nueva línea que no está en una cadena literal o después de ciertas palabras clave esnoequivalente al operador de punto y coma. Una lista de ;comandos simples delimitados sigue siendo unalista- como en el analizador del shell aún debe continuar leyendo los comandos simples que siguen a un ;comando simple delimitado antes de ejecutarse, mientras que una nueva línea puede delimitar una lista de comandos completa, o una lista de listas. La diferencia es sutil, pero complicada: dado que el shell no tiene un imperativo previo para leer datos después de una nueva línea, la nueva línea marca un punto donde el shell puede comenzar a evaluar los comandos simples que ya ha leído, mientras que un ;punto y coma sí lo hace . no.

  • &: Esto ejecutará un comando en segundo plano, lo que le permitirá continuar trabajando en el mismo shell.

       command1 & command2
    

Aquí, command1se inicia en segundo plano y command2comienza a ejecutarse en primer plano inmediatamente, sin esperar command1a salir.

Una nueva línea después command1es opcional.

A.2 Operadores lógicos

  • &&: Se utiliza para crear listas AND, le permite ejecutar un comando solo si otro salió exitosamente.

       command1 && command2
    

Aquí, command2se ejecutará después de command1que haya terminado ysolosi command1tuvo éxito (si su código de salida era 0). Ambos comandos se ejecutan en primer plano.

Este comando también se puede escribir

    if command1
    then command2
    else false
    fi

o simplemente if command1; then command2; fisi se ignora el estado de la devolución.

  • ||: Se utiliza para crear listas OR y le permite ejecutar un comando solo si otro salió sin éxito.

       command1 || command2
    

Aquí, command2solo se ejecutará si command1falla (si devolvió un estado de salida distinto de 0). Ambos comandos se ejecutan en primer plano.

Este comando también se puede escribir

    if command1
    then true
    else command2
    fi

o de forma más corta if ! command1; then command2; fi.

Tenga en cuenta que &&y ||son asociativos por izquierda; verPrioridad de los operadores lógicos del shell &&, ||para más información.

  • !: Esta es una palabra reservada que actúa como operador "no" (pero debe tener un delimitador), que se usa para negar el estado de retorno de un comando: devuelve 0 si el comando devuelve un estado distinto de cero, devuelve 1 si devuelve el estado 0 También es un NO lógico para la testutilidad.

      ! command1
    
      [ ! a = a ]
    

Y un verdadero operador NOT dentro de Expresiones Aritméticas:

    $ echo $((!0)) $((!23))
    1 0

A.3 Operador de tubería

  • |: El operador de tubería, pasa la salida de un comando como entrada a otro. Un comando creado a partir del operador de tubería se llamatubería.

       command1 | command2
    

    Cualquier salida impresa por command1se pasa como entrada a command2.

  • |&: Esta es una abreviatura de 2>&1 |bash y zsh. Pasa tanto la salida estándar como el error estándar de un comando como entrada a otro.

      command1 |& command2
    

A.4 Otra puntuación de lista

;;Se utiliza únicamente para marcar el final de unadeclaración del caso. Ksh, bash y zsh también permiten ;&pasar al siguiente caso y ;;&(no en ATT ksh) continuar y probar casos posteriores.

(y )estamos acostumbrados acomandos de grupoy lanzarlos en una subcapa. {y }también agrupar comandos, pero no los ejecute en un subshell. Veresta respuestapara una discusión sobre los distintos tipos de paréntesis, corchetes y llaves en la sintaxis del shell.

B. Operadores de redireccionamiento

Definición POSIX de operador de redirección

En el lenguaje de comandos del shell, un token que realiza una función de redirección. Es uno de los siguientes símbolos:

<     >     >|     <<     >>     <&     >&     <<-     <>

Estos le permiten controlar la entrada y salida de sus comandos. Pueden aparecer en cualquier lugar dentro de un comando simple o pueden seguir un comando. Las redirecciones se procesan en el orden en que aparecen, de izquierda a derecha.

  • <: Da entrada a un comando.

      command < file.txt
    

Lo anterior se ejecutará commanden el contenido de file.txt.

  • <>: igual que arriba, pero el archivo está abierto enleer+escribirmodo en lugar desolo lectura:

      command <> file.txt
    

Si el archivo no existe, se creará.

Ese operador rara vez se usa porque los comandos generalmente sololeerdesde su estándar, aunquePuede resultar útil en varias situaciones específicas..

  • >: dirige la salida de un comando a un archivo.

      command > out.txt
    

Lo anterior guardará la salida commandcomo out.txt. Si el archivo existe se sobrescribirá su contenido y si no existe se creará.

Este operador también se utiliza a menudo para elegir si algo se debe imprimir enError estándarosalida estándar:

    command >out.txt 2>error.txt
 

En el ejemplo anterior, >redirigirá la salida estándar y 2>redirigirá el error estándar. La salida también se puede redirigir usando 1>pero, dado que este es el valor predeterminado, 1generalmente se omite y se escribe simplemente como >.

Entonces, para ejecutar commandy file.txtguardar su salida out.txty cualquier mensaje de error, error.txtejecutaría:

    command < file.txt > out.txt 2> error.txt
  
  • >|: Hace lo mismo que >, pero sobrescribirá el destino, incluso si el shell se ha configurado para rechazar la sobrescritura (con set -Co set -o noclobber).

      command >| out.txt
    

Si out.txtexiste, la salida de commandreemplazará su contenido. Si no existe se creará.

  • >>: Hace lo mismo que >, excepto que si el archivo de destino existe, se agregan los nuevos datos.

      command >> out.txt
    

Si existe, se le agregará out.txtla salida de , después de lo que ya esté en él. commandSi no existe se creará.

  • >&: (según la especificación POSIX) cuando está rodeado dedígitos( 1>&2) o -en el lado derecho ( 1>&-) solo redirigeunodescriptor del archivo o lo cierra ( >&-).

Un >&número seguido de un número de descriptor de archivo es una forma portátil de redirigir un descriptor de archivo y >&-es una forma portátil de cerrar un descriptor de archivo.

Si el lado derecho de esta redirección es un archivo, lea la siguiente entrada.

  • >&, &>y : (lea también >>&arriba &>>) Redirige tanto el error estándar como la salida estándar, reemplazando o agregando, respectivamente.

      command &> out.txt
    

Tanto el error estándar como la salida estándar de commandse guardarán en out.txt, sobrescribiendo su contenido o creándolo si no existe.

    command &>> out.txt
 

Como se indicó anteriormente, excepto que, si existe, se le agregará out.txtla salida y el error de .command

La &>variante se origina en bash, mientras que la >&variante proviene de csh (décadas antes). Ambos entran en conflicto con otros operadores de shell POSIX y no deben usarse en shscripts portátiles.

  • <<: Un documento aquí. A menudo se utiliza para imprimir cadenas de varias líneas.

       command << WORD
           Text
       WORD
    

    Aquí, commandtomará todo hasta que encuentre la siguiente aparición de WORD, Texten el ejemplo anterior, como entrada. Si bien WORDes frecuente EoFo sus variaciones, puede ser cualquier cadena alfanumérica (y no solo) que desee. Cuando se cita o se escapa cualquier parte de WORD, el texto del documento aquí se trata literalmente y no se realizan expansiones (en variables, por ejemplo). Si no está entre comillas, las variables se expandirán. Para más detalles, consulte elmanual de bash.

    Si desea canalizar la salida de command << WORD ... WORDdirectamente a otro comando o comandos, debe colocar la canalización en la misma línea que << WORD, no puede colocarla después de la PALABRA final o en la línea siguiente. Por ejemplo:

       command << WORD | command2 | command3...
           Text
       WORD
    
  • <<<: Aquí cadenas, similares a los documentos aquí, pero destinadas a una sola línea. Estos existen sólo en el puerto Unix o rc (donde se originó), zsh, algunas implementaciones de ksh, yash y bash.

      command <<< WORD
    

Todo lo que se proporciona WORDse expande y su valor se pasa como entrada a command. Esto se usa a menudo para pasar el contenido de variables como entrada a un comando. Por ejemplo:

     $ foo="bar"
     $ sed 's/a/A/' <<< "$foo"
     bAr
     # as a short-cut for the standard:
     $ printf '%s\n' "$foo" | sed 's/a/A/'
     bAr
     # or
     sed 's/a/A/' << EOF
     $foo
     EOF

Se pueden utilizar algunos otros operadores ( >&-, ) para cerrar o duplicar descriptores de archivos. x>&y x<&yPara obtener detalles sobre ellos, consulte la sección correspondiente del manual de su shell (aquípor ejemplo para bash).

Esto sólo cubre los operadores más comunes de proyectiles tipo Bourne. Algunos shells tienen algunos operadores de redirección adicionales propios.

Ksh, bash y zsh también tienen construcciones <(…), >(…)y =(…)(esta última zshsolo). Estas no son redirecciones, sinosustitución de procesos.

Respuesta2

Advertencia sobre '>'

Los principiantes de Unix que acaban de aprender sobre la redirección de E/S ( <y >) a menudo prueban cosas como

dominiofichero de entrada>el_mismo_archivo

o

dominio… <archivo     >el_mismo_archivo

o, casi equivalentemente,

gatoarchivo|dominio… >el_mismo_archivo

( grep, sed, cut, sorty spellson ejemplos de comandos que las personas se sienten tentadas a usar en construcciones como estas). Los usuarios se sorprenden al descubrir que estos escenarios dan como resultado que el archivo quede vacío.

Un matiz que no parece mencionarse en la otra respuesta se puede encontrar escondido en la primera oración de laRedirecciónSección defiesta(1):

Antes de ejecutar un comando, su entrada y salida pueden serredirigido usando una notación especial interpretada por el shell.

Las primeras cinco palabras deben estar en negrita, cursiva, subrayadas, ampliadas, parpadeantes, de color rojo y marcadas con un signo de exclamación en triángulo rojoícono, para enfatizar el hecho de que el shell realiza las redirecciones solicitadas. antes de que se ejecute el comando. Y recuerda también

La redirección de la salida hace que el archivo... se abra para escribir... Si el archivo no existe, se crea; si existe, se trunca a tamaño cero.

  1. Entonces, en este ejemplo:

    sort roster > roster
    

    el shell abre el rosterarchivo para escribirlo, truncándolo (es decir, descartando todo su contenido), antes de que el sortprograma comience a ejecutarse. Naturalmente, no se puede hacer nada para recuperar los datos.

  2. Uno podría esperar ingenuamente que

    tr "[:upper:]" "[:lower:]" < poem > poem
    

    podría ser mejor. Debido a que el shell maneja redirecciones de izquierda a derecha, se abre poempara lectura (para trla entrada estándar) antes de abrirlo para escritura (para la salida estándar). Pero no ayuda. Aunque esta secuencia de operaciones genera dos identificadores de archivos, ambos apuntan al mismo archivo. Cuando el shell abre el archivo para leerlo, el contenido sigue ahí, pero sigue siendo golpeado antes de que se ejecute el programa. 

Entonces, ¿qué hacer al respecto?

Las soluciones incluyen:

  • Compruebe si el programa que está ejecutando tiene su propia capacidad interna para especificar dónde va la salida. Esto suele indicarse mediante una ficha -o(o --output=). En particular,

    sort -o roster roster
    

    es aproximadamente equivalente a

    sort roster > roster
    

    excepto que, en el primer caso, el sortprograma abre el archivo de salida. Y es lo suficientemente inteligente como para no abrir el archivo de salida hastadespuésha leído todos los archivos de entrada.

    De manera similar, al menos algunas versiones de sedtienen un -i(editarin lugar) opción que se puede utilizar para escribir la salida nuevamente en el archivo de entrada (nuevamente,despuésse han leído todas las entradas). Editores como ed/ ex, emacs, picoy vi/ vim permiten al usuario editar un archivo de texto y guardar el texto editado en el archivo original. Tenga en cuenta que ed(al menos) se puede utilizar de forma no interactiva.

    • vitiene una característica relacionada. Si escribe , escribirá el contenido del búfer de edición en:%!commandEntercommand, lea el resultado e insértelo en el búfer (reemplazando el contenido original).
  • Sencillo pero eficaz:

    dominiofichero de entrada>archivo_temporal  && mvarchivo_temporal fichero de entrada

    Esto tiene el inconveniente de que, siinput_filees un enlace, (probablemente) será reemplazado por un archivo separado. Además, el nuevo archivo será de su propiedad, con protecciones predeterminadas. En particular, esto conlleva el riesgo de que el archivo acabe siendo legible en todo el mundo, incluso si el originalinput_fileno lo fue.

    Variaciones:

    • commandinput_file > temp_file && cp temp_file input_file && rm temp_file
      que todavía (potencialmente) dejará eltemp_filelegible en todo el mundo. Aun mejor:
    • cp input_file temp_file && commandtemp_file > input_file && rm temp_file
      Estos preservan el estado del enlace, el propietario y el modo (protección) del archivo, potencialmente a costa del doble de E/S. (Es posible que necesite utilizar una opción como -ao -pon cp para indicarle que conserve los atributos).
    • commandinput_file > temp_file &&
      cp --attributes-only --preserve=all input_file temp_file &&
      mv temp_file input_file
      (dividido en líneas separadas solo para facilitar la lectura) Esto conserva el modo del archivo (y, si es root, el propietario), pero lo convierte en su propiedad (si no es root) y lo convierte en un archivo nuevo. archivo separado.
  • este blog (Edición de archivos "in situ") sugiere y explica

    { salafichero de entrada  &&  dominio… >fichero de entrada; } <fichero de entrada

    Esto requiere que elcommandser capaz de procesar entradas estándar (pero casi todos los filtros pueden hacerlo). El propio blog califica esto como una pifia arriesgada y desaconseja su uso. Y esto también creará un archivo nuevo e independiente (no vinculado a nada), de su propiedad y con permisos predeterminados.

  • El paquete moreutils tiene un comando llamado sponge:

    dominiofichero de entrada| esponjael_mismo_archivo

    Veresta respuestapara más información.

Aquí hay algo que me sorprendió por completo: error de sintaxis dice:

[La mayoría de estas soluciones] fallarán en un sistema de archivos de solo lectura, donde "solo lectura" significa que su$HOME voluntadser escribible, pero /tmpserásolo lectura(por defecto). Por ejemplo, si tiene Ubuntu y ha iniciado la Consola de recuperación, este suele ser el caso. Además, el operador aquí-documento <<<tampoco funcionará allí, ya que requiere /tmpserleer escribir porque también escribirá un archivo temporal allí.
(cf.esta preguntaincluye una stracesalida 'd)

Lo siguiente puede funcionar en ese caso:

  • Sólo para usuarios avanzados: Si se garantiza que su comando producirá la misma cantidad de datos de salida que de entrada (por ejemplo, sortotr sinla opción -do -s), puedes probar
    dominiofichero de entrada| dd de =el_mismo_archivoconv=notrunc
    Veresta respuesta yesta respuestapara obtener más información, incluida una explicación de lo anterior, y alternativas que funcionan si se garantiza que su comando producirá la misma cantidad de datos de salida que de entradao menos(p. ej. grep, o cut). Estas respuestas tienen la ventaja de que no requieren espacio libre (o requieren muy poco). Las respuestas anteriores del formulario requieren claramente que haya suficiente espacio libre para que el sistema pueda contener todo el archivo de entrada (antiguo) y el archivo de salida (nuevo) simultáneamente; Esto no es obviamente cierto también para la mayoría de las otras soluciones (por ejemplo, y ). Excepción: probablemente requerirá mucho espacio libre, porque necesita leer todas sus entradas antes de poder escribir cualquier salida, y probablemente almacene la mayoría, si no todos, esos datos en un archivo temporal.commandinput_file > temp_file && …sed -ispongesort … | dd …sort
  • Sólo para usuarios avanzados:
    dominiofichero de entrada1<>el_mismo_archivo
    puede ser equivalente a la ddrespuesta anterior. La sintaxis abre el archivo nombrado en el descriptor de archivo.n<> filen tanto para entrada como para salida, sin truncarlo, una especie de combinación de y . Nota: Algunos programas (por ejemplo, y ) pueden negarse a ejecutarse en este escenario porque pueden detectar que la entrada y la salida son el mismo archivo. Vern<n>catgrepesta respuesta para una discusión de lo anterior, y un script que hace que esta respuesta funcione si se garantiza que su comando producirá la misma cantidad de datos de salida que de entradao menos.
    Advertencia: no he probado el guión de Peter, así que no lo garantizo.

¿Entonces, cuál era la pregunta?

Este ha sido un tema popular en U&L; se aborda en las siguientes preguntas:

… y eso sin contar Superusuario o Ask Ubuntu. He incorporado mucha de la información de las respuestas a las preguntas anteriores aquí en esta respuesta, pero no toda. (Es decir, para obtener más información, lea las preguntas mencionadas anteriormente y sus respuestas).

PD: tengoNoafiliación con el blog que cité arriba.

Respuesta3

Más observaciones sobre ;, &, (y)

  • Tenga en cuenta que algunos de los comandos de la respuesta de terdon pueden ser nulos. Por ejemplo, puedes decir

    command1 ;
    

    (con ningún command2). Esto es equivalente a

    command1
    

    (es decir, simplemente se ejecuta command1en primer plano y espera a que se complete. De manera similar,

    command1 &
    

    (sin command2) se iniciará command1en segundo plano y luego emitirá otro mensaje de shell inmediatamente.

  • Por el contrario, command1 &&, command1 ||y command1 |no tienen ningún sentido. Si escribe uno de estos, el shell (probablemente) asumirá que el comando continúa en otra línea. Mostrará el indicador de shell secundario (continuación), que normalmente está configurado en >y seguirá leyendo. En un script de shell, simplemente leerá la siguiente línea y la agregará a lo que ya leyó. (Cuidado: puede que esto no sea lo que desea que suceda).

    Nota: algunas versiones de algunos shells pueden tratar estos comandos incompletos como errores. En tales casos (o, de hecho, encualquierEn el caso de que tenga un comando largo), puede colocar una barra invertida ( \) al final de una línea para indicarle al shell que continúe leyendo el comando en otra línea:

    command1  &&  \
    command2
    

    o

    find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
                            -newer some_existing_file -user fred -readable -print
    
  • Como dice terdon, (y )se puede usar para agrupar comandos. La afirmación de que “no son realmente relevantes” para esa discusión es discutible. Algunos de los comandos en la respuesta de terdon pueden ser comandosgrupos. Por ejemplo,

    ( command1 ; command2 )  &&  ( command3; command4 )
    

    Haz esto:

    • Corre command1y espera a que termine.
    • Luego, independientemente del resultado de ejecutar ese primer comando, ejecútelo command2y espere a que finalice.
    • Entonces, si command2tuvo éxito,

      • Corre command3y espera a que termine.
      • Luego, independientemente del resultado de ejecutar ese comando, ejecútelo command4y espere a que finalice.

      Si command2falla, deje de procesar la línea de comando.

  • Fuera del paréntesis, |se une muy estrechamente, por lo que

    command1 | command2 || command3
    

    es equivalente a

    ( command1 | command2 )  ||  command3
    

    y &&atar ||más fuerte que ;, entonces

    command1 && command2 ; command3
    

    es equivalente a

    ( command1 && command2 ) ;  command3
    

    es decir, command3se ejecutará independientemente del estado de salida de command1y/o command2.

información relacionada