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


247

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. .

Respuestas:


340

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

A. Operadores de control

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

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

Y |&en bash.

A no! es un operador de control sino una Palabra reservada . Se convierte en un NOT lógico [operador de negación] dentro de Expresiones aritméticas y dentro de construcciones de prueba (aunque aún requiere un delimitador de espacio).

A.1 Lista de terminadores

  • ; : Ejecutará un comando después de que otro haya terminado, independientemente del resultado del primero.

    command1 ; command2

    Primero command1se ejecuta, en primer plano, y una vez que haya terminado, command2se ejecutará.

    Una nueva línea que no está en un literal de cadena o después de ciertas palabras clave no es equivalente al operador de punto y coma. Una lista de ;comandos simples delimitados sigue siendo una lista , ya que en el analizador del shell todavía debe seguir leyendo los comandos simples que siguen a un ;comando simple delimitado antes de ejecutar, 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í no.

  • & : Esto ejecutará un comando en segundo plano, permitiéndole 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ó con éxito.

     command1 && command2

    Aquí, command2se ejecutará después de que command1haya terminado y solo si command1fue exitoso (si su código de salida fue 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 devolución.

  • || : Se utiliza para crear listas OR, 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 una manera más corta if ! command1; then command2; fi.

    Tenga en cuenta que &&y ||son asociativos a la izquierda; ver Precedencia de los operadores lógicos de shell &&, || para más información.

  • !: Esta es una palabra reservada que actúa como el operador "no" (pero debe tener un delimitador), utilizada 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 un NO lógico para la testutilidad.

    ! command1
    
    [ ! a = a ]

    Y un verdadero operador NO 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 construido desde el operador de tubería se llama tuberí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 Otros signos de puntuación

;;se usa únicamente para marcar el final de una declaración de caso . Ksh, bash y zsh también admiten ;&pasar al siguiente caso y ;;&(no en ATT ksh) continuar y probar casos posteriores.

(y )se usan para agrupar comandos y ejecutarlos en una subshell. {y }también agrupe comandos, pero no los inicie en una subshell. Vea esta respuesta para una discusión de los diversos tipos de paréntesis, corchetes y llaves en la sintaxis de shell.

B. Operadores de redireccionamiento

Operador de redireccionamiento

En el lenguaje de comandos de shell, un token que realiza una función de redireccionamiento. 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 los contenidos de file.txt.

  • <>: igual que el anterior, pero el archivo está abierto en modo lectura + escritura en lugar de solo lectura :

    command <> file.txt

    Si el archivo no existe, se creará.

    Ese operador rara vez se usa porque los comandos generalmente solo leen su stdin, aunque puede ser útil en una serie de situaciones específicas .

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

    command > out.txt

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

    Este operador también se usa a menudo para elegir si algo debe imprimirse con error estándar o salida 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 >.

    Así que, para funcionar commanden file.txty guardar en su salida out.txty mensajes de error en error.txtque permiten la ejecución:

    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 datos nuevos.

    command >> out.txt

    Si out.txtexiste, la salida de commandse agregará a él, después de lo que ya esté en él. Si no existe, se creará.

  • &>, >&, >>&Y &>>: (no estándar). Redireccione 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 out.txt, sobrescribiendo su contenido o creándolo si no existe.

    command &>> out.txt

    Como arriba, excepto que si out.txtexiste, commandse le agregará la salida y el error .

    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 usa para imprimir cadenas de varias líneas.

     command << WORD
         Text
     WORD

    Aquí, commandtomará todo hasta que encuentre la próxima aparición de WORD, Texten el ejemplo anterior, como entrada. Si bien WORDes a menudo EoFo variaciones de los mismos, puede ser cualquier cadena alfanumérica (y no solo) que desee. Cuando WORDse cita, el texto en el 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 el manual de bash .

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

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

    command <<< WORD

    Lo que se da como 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

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

Eso solo cubre los operadores más comunes de los proyectiles tipo Bourne. Algunos shells tienen algunos operadores de redireccionamiento adicionales propios.

Ksh, bash y zsh también tienen constructos <(…), >(…)y =(…)(ese último zshsolo). No se trata de redireccionamientos, sino de sustitución de procesos .


2
Probablemente valdría la pena señalar que no todos los shells son iguales y destacar específicamente las características específicas de bash.
Greg Hewgill

1
@GregHewgill, sí, me liberé diciendo que estoy discutiendo con respecto a bash. Esto se está preparando como una sesión de preguntas y respuestas canónicas para cerrar las diversas preguntas de "¿Qué hace esta cosita rara?" Y la mayoría de ellas son de usuarios de bash. Espero que alguien más intervenga y responda por shells que no sean bash, pero resaltar los específicos de bash tiene mucho sentido. Sin embargo, tendré que verificar, no sé cuáles están fuera de mi cabeza.
terdon

&>, >>>Y <<<son todas para no POSIX como es la referencia a la no-únicos caracteres no ALPHANUM en nombre de una de aquí-doc. Esta respuesta también discute muy poco sobre cómo funcionan; por ejemplo, es casi peor que inútil hablar sobre un comando simple y un comando sin explicar cuáles son y cómo decide el shell.
mikeserv

@mikeserv gracias. Sin embargo, trabajan en bash y zsh. No sé qué, si es que hay algo, es realmente específico de bash en esa lista. Debería pasar por esto y agregar las conchas en las que trabaja cada una, pero eso implicaría averiguarlo primero.
terdon

1
@ Arc676 No, no evalúan como verdadero o falso, ese es un contexto completamente diferente. Esto solo significa que un valor de salida distinto de 0 indica un problema (no false) y un código de salida 0 indica éxito (no true). Ese siempre ha sido el camino y es bastante estándar. Un código de salida distinto de 0 indica un error en todos los entornos que conozco.
terdon

61

Advertencia sobre '>'

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

comando ... archivo_entrada > the_same_file

o

comando ... < archivo      > the_same_file

o, casi equivalentemente,

archivo de gato | comando ...> the_same_file

( grep, sed, cut, sort, Y spellson ejemplos de comandos que las personas se ven tentados a utilizar en construcciones como éstas.) Los usuarios son sorprendidos al descubrir que estos escenarios resultan en el archivo se vacíe.

Un matiz que no parece mencionarse en la otra respuesta se puede encontrar al acecho en la primera oración de la sección Redirección de bash (1) :

Antes de ejecutar un comando, su entrada y salida se pueden redirigir utilizando 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 rojoicono, para enfatizar el hecho de que el shell realiza las redirecciones solicitadas antes de ejecutar 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 escribir, 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 las redirecciones de izquierda a derecha, se abre poempara leer (para trla entrada estándar) antes de abrirlo para escribir (para la salida estándar). Pero no ayuda. Aunque esta secuencia de operaciones produce dos identificadores de archivo, ambos apuntan al mismo archivo. Cuando el shell abre el archivo para leerlo, el contenido todavía está allí, pero aún así se bloquea 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 a dónde va la salida. Esto a menudo se indica mediante una -o(o --output=) ficha. En particular,

    sort roster -o roster

    es más o menos equivalente a

    sort roster > roster

    excepto, en el primer caso, el sortprograma abre el archivo de salida. Y es suficiente para no abrir el archivo de salida inteligente hasta que después de haber leído todo el archivo (s) de entrada.

    Del mismo modo, al menos algunas versiones de sedtienen un -i(edición i n Place) opción que se puede utilizar para escribir el resultado de vuelta al archivo de entrada (de nuevo, después de todo la entrada de haber sido leído). 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) puede usarse de forma no interactiva.

    • viTiene una característica relacionada. Si escribe , escribirá el contenido del búfer de edición , leerá el resultado y lo insertará en el búfer (reemplazando el contenido original).:%!commandEntercommand
  • Simple pero efectivo:

    comando ... archivo_entrada > temp_file   && mv temp_file  archivo_entrada

    Esto tiene el inconveniente de que, si input_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 termine siendo legible en todo el mundo, incluso si el original input_fileno lo fuera.

    Variaciones:

    • commandinput_file > temp_file && cp temp_file input_file && rm temp_file
      que todavía (potencialmente) dejará el temp_filemundo legible. Aun mejor:
    • cp input_file temp_file && commandtemp_file > input_file && rm temp_file
      Éstos 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 usar una opción como -ao -pen cp contarla para preservar 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 hace suyo (si no es root), y lo convierte en un nuevo, archivo separado
  • Este blog (edición "in situ" de archivos) sugiere y explica

    {rm input_file   &&   command ...> input_file ; } < input_file

    Esto requiere que commandse pueda procesar la entrada estándar (pero casi todos los filtros pueden). El propio blog llama a esto un riesgo arriesgado y desalienta su uso. Y esto también creará un archivo nuevo y separado (no vinculado a nada), propiedad de usted y con permisos predeterminados.

  • El paquete moreutils tiene un comando llamado sponge:

    comando ... input_file | esponja the_same_file

    Vea esta respuesta para más información.

Aquí hay algo que me sorprendió por completo: syntaxerror dice :

[La mayoría de estas soluciones] fallará en un sistema de archivos de solo lectura, donde “solo lectura” significa que $HOME se podrá escribir, pero /tmpserá de solo lectura (por defecto). Por ejemplo, si tiene Ubuntu y ha arrancado en la Consola de recuperación, este suele ser el caso. Además, el operador de documento aquí <<<tampoco funcionará allí, ya que requiere /tmpser leído / escrito porque también escribirá un archivo temporal allí.
(cf. esta pregunta incluye una strace'd salida)

Lo siguiente puede funcionar en ese caso:

  • Solo para usuarios avanzados: si se garantiza que su comando producirá la misma cantidad de datos de salida que hay entrada (por ejemplo sort, o tr sin la opción -do -s), puede intentar
    comando ... input_file | dd of = the_same_file conv = notrunc
    Consulte esta respuesta y esta respuesta para obtener más información, incluida una explicación de lo anterior, y alternativas que funcionan si se garantiza que su comando produzca la misma cantidad de datos de salida que hay entrada o menos (por ejemplo 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 de salida (nuevo) simultáneamente; Esto no es obviamente cierto para la mayoría de las otras soluciones (por ejemplo, y ) también. Excepción: probablemente requerirá mucho espacio libre, porquecommandinput_file > temp_file && …sed -ispongesort … | dd …sort necesita leer toda su entrada antes de que pueda escribir cualquier salida, y probablemente almacena la mayoría de los datos, si no todos, en un archivo temporal.
  • Solo para usuarios avanzados:
    comando ... input_file 1 <> the_same_file
    puede ser equivalente a la ddrespuesta anterior. La sintaxis abre el archivo con nombre en el descriptor de archivo para entrada y 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. Vea esta respuesta para una discusión de lo anterior, y un script que hace que esta respuesta funcione si se garantiza que su comando produzca la misma cantidad de datos de salida que hay entrada o menos . Advertencia: no he probado el guión de Peter, así que no respondo por ello.n<> filen n<n>catgrep

¿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 Super User o Ask Ubuntu. He incorporado mucha de la información de las respuestas a las preguntas anteriores aquí en esta respuesta, pero no todas. (Es decir, para obtener más información, lea las preguntas mencionadas anteriormente y sus respuestas).

PD tiene ninguna afiliación con el blog que he citado más arriba.


Como esta pregunta sigue apareciendo, pensé en intentar escribir una "respuesta canónica". ¿Debería publicarlo aquí (y tal vez vincularlo desde alguna de las otras preguntas con mayor tráfico), o debería moverlo a una de las preguntas que realmente plantea este problema? Además, ¿es esta una situación en la que las preguntas deberían fusionarse?
Scott

/ tmp Un directorio disponible para aplicaciones que necesitan un lugar para crear archivos temporales. Se permitirá a las aplicaciones crear archivos en este directorio, pero no se supondrá que dichos archivos se conservan entre invocaciones de la aplicación.
mikeserv

@mikeserv: Sí, (1) estoy citando Syntaxerror, y (2) dije que estaba sorprendido. Pensé que, si algo sería de lectura-escritura, sería /tmp.
Scott

Bueno, lo que dijo @syntaxerror es doblemente extraño porque, como creo, dashsería el shell de recuperación predeterminado en Ubuntu y no solo no entiende una <<<herejía, sino que también obtiene tuberías anónimas para los <<documentos aquí y no se mete con ${TMPDIR:-/tmp}eso. propósito en absoluto. Vea esto o esto para ver demostraciones sobre el manejo de documentos aquí. ¿También por qué la misma cantidad de salida o menos advertencia?
mikeserv

@mikeserv: Bueno, los dd … conv=notruncy las 1<>respuestas no truncar el archivo de salida, por lo que, si la salida del comando es menor que la entrada (por ejemplo, grep), habrá algunos bytes del sobrante al final del archivo original. Y, si la salida es mayor que el de entrada (por ejemplo, cat -n, nl, o (potencialmente) grep -n), hay un riesgo de sobrescribir los datos antiguos antes de que lo has leído.
Scott

29

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

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

    command1 ;

    (sin command2) Esto es equivalente a

    command1

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

    command1 &

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

  • Por el contrario, command1 &&, command1 ||, y command1 |no tiene ningún sentido. Si escribe uno de estos, el shell (probablemente) supondrá 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, solo leerá la siguiente línea y la agregará a lo que ya ha leído. (Cuidado: esto podría no ser lo que quieres que suceda).

    Nota: algunas versiones de algunos shells pueden tratar estos comandos incompletos como errores. En tales casos (o, de hecho, en cualquier caso donde 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 )puede usarse 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 grupos de comandos . 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 termine.
    • Entonces, si tiene command2éxito,

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

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

  • Fuera de los paréntesis, se |une muy fuerte, así que

    command1 | command2 || command3

    es equivalente a

    ( command1 | command2 )  ||  command3

    y &&y ||unirse más apretado que ;, por lo

    command1 && command2 ; command3

    es equivalente a

    ( command1 && command2 ) ;  command3

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


Perfecto, +1! Dije que no eran relevantes porque no quería entrar en tantos detalles. Quería una respuesta que pudiera funcionar como una hoja de trucos rápida para los novatos que se preguntan cuáles son todos los garabatos extraños al final de los diversos comandos. No quise decir que no son útiles. Gracias por agregar todo esto.
terdon

1
Me preocupa el problema de la "masa crítica": si publicamos todo lo que podríamos decir sobre los proyectiles, terminaremos con nuestra propia versión TL; DR del Manual de referencia de Bash.
G-Man

También vale la pena mencionar: a diferencia de los lenguajes de la familia C, ;por sí solo (o sin un comando que lo precede) es un error de sintaxis, y no una declaración vacía. Por ; ;lo tanto, es un error. (Una trampa común para los nuevos usuarios, en mi humilde opinión). Además: ;;es un delimitador especial, para casedeclaraciones.
muru

1
@muru: Buen punto, pero generalizémoslo. Cualquier de los operadores de control que pueden aparecer entre los comandos: ;, &&, ||, &, y |, son errores si aparecen sin nada que les precede. Además, terdon se dirigió ;;(brevemente) en su respuesta.
G-Man

1
@Wildcard: OK, veo de dónde vienes. La palabra clave es "mayo"; Todo lo que decía era que no garantizo que todos los shells aceptarán tales construcciones (es decir, YMMV). Obviamente escribí eso antes de saber sobre el uso del linebreaktoken en la gramática de shell POSIX. Entonces, quizás sea seguro decir que todos los shells compatibles con POSIX los aceptarán. Mantengo mi declaración como un descargo de responsabilidad general; Si encuentra un shell pre-POSIX suficientemente viejo, como un shell Bourne real o más antiguo, todas las apuestas están desactivadas.
G-Man
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.