Estoy un poco confundido sobre qué hacen estos operadores de manera diferente cuando se usan en bash (corchetes, corchetes dobles, paréntesis y paréntesis dobles).
[[ , [ , ( , ((
He visto a personas usarlas en declaraciones if como esta:
if [[condition]]
if [condition]
if ((condition))
if (condition)
Respuestas:
Una
if
declaración típicamente se parece aLa
then
cláusula se ejecuta si el código de salida decommands1
es cero. Si el código de salida no es cero,else
se ejecuta la cláusula.commands1
puede ser simple o complejo Se puede, por ejemplo, ser una secuencia de uno o más tuberías separadas por uno de los operadores;
,&
,&&
, o||
. Lasif
condiciones que se muestran a continuación son solo casos especiales decommands1
:if [ condition ]
Este es el
test
comando de shell tradicional . Está disponible en todos los shells POSIX. El comando de prueba establece un código de salida y laif
instrucción actúa en consecuencia. Las pruebas típicas son si existe un archivo o si un número es igual a otro.if [[ condition ]]
Esta es una nueva variación mejorada
test
de ksh que bash y zsh también admiten. Estetest
comando también establece un código de salida y laif
instrucción actúa en consecuencia. Entre sus características extendidas, puede probar si una cadena coincide con una expresión regular.if ((condition))
Otra extensión de ksh que también admiten bash y zsh . Esto realiza aritmética. Como resultado de la aritmética, se establece un código de salida y la
if
instrucción actúa en consecuencia. Devuelve un código de salida de cero (verdadero) si el resultado del cálculo aritmético es distinto de cero. Al igual[[...]]
, este formulario no es POSIX y, por lo tanto, no es portátil.if (command)
Esto ejecuta el comando en una subshell. Cuando se completa el comando, establece un código de salida y la
if
instrucción actúa en consecuencia.Una razón típica para el uso de un subnivel de este tipo es para limitar los efectos secundarios de
command
sicommand
se requiere asignaciones de variables u otros cambios en el entorno de la cáscara. Tales cambios no permanecen después de que se completa la subshell.if command
El comando se ejecuta y la
if
instrucción actúa de acuerdo con su código de salida.fuente
[
realidad es un binario, no un comando o símbolo interno. Generalmente vive en/bin
.[
está integrado, tal como estátest
. Hay versiones binarias disponibles por razones de compatibilidad. Echa un vistazohelp [
yhelp test
.$((
es decir , la expansión aritmética es y es fácil confundirlos. A menudo, una solución alternativa es usar algo como[ $((2+2)) -eq 4 ]
hacer uso de la aritmética en declaraciones condicionales(…)
los paréntesis indican una subshell . Lo que hay dentro de ellos no es una expresión como en muchos otros idiomas. Es una lista de comandos (al igual que los paréntesis externos). Estos comandos se ejecutan en un subproceso separado, por lo que cualquier redirección, asignación, etc. realizada dentro de los paréntesis no tiene efecto fuera de los paréntesis.$(…)
es una sustitución de comando : hay un comando dentro de los paréntesis, y la salida del comando se usa como parte de la línea de comando (después de expansiones adicionales a menos que la sustitución sea entre comillas dobles, pero esa es otra historia ) .{ … }
Las llaves son como paréntesis en el sentido de que agrupan comandos, pero solo influyen en el análisis, no en la agrupación. El programax=2; { x=4; }; echo $x
imprime 4, mientras quex=2; (x=4); echo $x
imprime 2. (También los corchetes que son palabras clave deben delimitarse y encontrarse en la posición del comando (de ahí el espacio después{
y;
antes}
), mientras que los paréntesis no. Eso es solo una peculiaridad de sintaxis).${VAR}
es una expansión de parámetros , que se expande al valor de una variable, con posibles transformaciones adicionales. Elksh93
shell también admite una${ cmd;}
forma de sustitución de comandos que no genera un subshell.((…))
los paréntesis dobles rodean una instrucción aritmética , es decir, un cálculo en enteros, con una sintaxis similar a otros lenguajes de programación. Esta sintaxis se usa principalmente para asignaciones y en condicionales. Esto solo existe en ksh / bash / zsh, no en sh simple.$((…))
, que se expanden al valor entero de la expresión.[ … ]
Los corchetes simples rodean las expresiones condicionales . Las expresiones condicionales se basan principalmente en operadores como-n "$variable"
para probar si una variable está vacía y-e "$file"
para probar si existe un archivo. Tenga en cuenta que necesita un espacio alrededor de cada operador (por ejemplo[ "$x" = "$y" ]
, no), y un espacio o un carácter como[ "$x"="$y" ]
;
dentro y fuera de los corchetes (por ejemplo[ -n "$foo" ]
, no).[-n "$foo"]
[[ … ]]
los corchetes dobles son una forma alternativa de expresiones condicionales en ksh / bash / zsh con algunas características adicionales, por ejemplo, puede escribir[[ -L $file && -f $file ]]
para probar si un archivo es un enlace simbólico a un archivo normal, mientras que los corchetes simples requieren[ -L "$file" ] && [ -f "$file" ]
. Consulte ¿Por qué la expansión de parámetros con espacios sin comillas funciona entre corchetes dobles [[pero no entre corchetes [? para más sobre este temaEn el shell, cada comando es un comando condicional: cada comando tiene un estado de retorno que es 0 que indica éxito o un número entero entre 1 y 255 (y potencialmente más en algunos shells) que indica falla. El
[ … ]
comando (o[[ … ]]
forma de sintaxis) es un comando particular que también se puede deletreartest …
y tiene éxito cuando existe un archivo, o cuando una cadena no está vacía, o cuando un número es más pequeño que otro, etc. La((…))
forma de sintaxis tiene éxito cuando un número es distinto de cero Aquí hay algunos ejemplos de condicionales en un script de shell:Prueba si
myfile
contiene la cadenahello
:Si
mydir
es un directorio, cámbielo y haga cosas:Pruebe si hay un archivo llamado
myfile
en el directorio actual:Lo mismo, pero también incluye enlaces simbólicos colgantes:
Pruebe si el valor de
x
(que se supone que es numérico) es al menos 2, de forma portátil:Pruebe si el valor de
x
(que se supone que es numérico) es al menos 2, en bash / ksh / zsh:fuente
-a
lugar de&&
, por lo que se puede escribir:,[ -L $file -a -f $file ]
que es el mismo número de caracteres dentro de los corchetes sin el extra[
y]
...-a
y-o
son problemáticos porque pueden conducir a análisis incorrectos si algunos de los operandos involucrados parecen operadores. Por eso no los menciono: tienen cero ventajas y no siempre funcionan. Y nunca escriba expansiones de variables sin comillas sin una buena razón:[[ -L $file -a -f $file ]]
está bien, pero con paréntesis individuales necesita[ -L "$file" -a -f "$file" ]
(lo cual está bien, por ejemplo, si$file
siempre comienza con/
o./
).[[ -L $file && -f $file ]]
(no-a
con la[[...]]
variante).De la documentación de bash :
En otras palabras, se asegura de que lo que suceda en 'lista' (como a
cd
) no tenga ningún efecto fuera de(
y)
. La única cosa que se escape es el código de salida del último comando o conset -e
el primer comando que genera un error (aparte de unos pocos, comoif
,while
, etc.)Esta es una extensión bash que te permite hacer matemáticas. Esto es algo similar a usar
expr
sin todas las limitaciones deexpr
(como tener espacios en todas partes, escapar*
, etc.)Esto ofrece una prueba avanzada para comparar cadenas, números y archivos un poco como las
test
ofertas, pero más potente.Este llama
test
. En realidad, en los viejos tiempos,[
era un enlace simbólico atest
. Funciona de la misma manera y tienes las mismas limitaciones. Dado que un binario conoce el nombre con el que se inició, el programa de prueba puede analizar los parámetros hasta que encuentre un parámetro]
. Divertidos trucos de Unix.Tenga en cuenta que, en el caso de
bash
,[
ytest
son funciones integradas (como se menciona en un comentario), se aplican prácticamente las mismas limitaciones.fuente
test
y[
, por supuesto, son comandos incorporados en Bash, pero es probable que también exista un binario externo.[
no es un enlace simbólicotest
en la mayoría de los sistemas modernos.strings /usr/bin/test
muestra que también tiene el texto de ayuda, así que no sé qué decir.test
estándar obviamente requiere que exista como un comando independiente basado en un archivo según el estándar, nada en él indica que su[
variante deba implementarse de esa manera también. Por ejemplo, Solaris 11 no proporciona ningún[
ejecutable pero, sin embargo, es totalmente compatible con los estándares POSIX[
vs[[
Esta respuesta cubrirá el subconjunto
[
vs[[
de la pregunta.Algunas diferencias en Bash 4.3.11:
Extensión POSIX vs Bash:
[
es POSIX[[
es una extensión Bash¹ documentada en: https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructscomando regular vs magia
[
es solo un comando regular con un nombre extraño.]
es solo un argumento[
que evita que se utilicen más argumentos.Ubuntu 16.04 en realidad tiene un ejecutable para él
/usr/bin/[
proporcionado por coreutils, pero la versión integrada de bash tiene prioridad.Nada se altera en la forma en que Bash analiza el comando.
En particular,
<
es la redirección&&
y||
concatena múltiples comandos,( )
genera subcapas a menos que se escapen\
y la expansión de palabras ocurre como de costumbre.[[ X ]]
es una construcción única que hace queX
se analice mágicamente.<
,&&
,||
Y()
se tratan de forma especial, y las reglas de división de palabras son diferentes.También hay otras diferencias como
=
y=~
.En Bashese:
[
es un comando incorporado y[[
es una palabra clave: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword<
[[ a < b ]]
: comparación lexicográfica[ a \< b ]
: Lo mismo que arriba.\
requerido o de lo contrario hace la redirección como para cualquier otro comando. Bash extension.expr a \< b > /dev/null
: POSIX equivalente², ver: https://stackoverflow.com/questions/21294867/how-to-test-strings-for-lexicographic-less-than-or-equal-in-bash/52707989#52707989&&
y||
[[ a = a && b = b ]]
: verdadero, lógico y[ a = a && b = b ]
: error de sintaxis,&&
analizado como un separador de comando ANDcmd1 && cmd2
[ a = a -a b = b ]
: equivalente, pero obsoleto por POSIX³[ a = a ] && [ b = b ]
: POSIX y equivalente confiable(
[[ (a = a || a = b) && a = b ]]
: falso[ ( a = a ) ]
: error de sintaxis,()
se interpreta como una subshell[ \( a = a -o a = b \) -a a = b ]
: equivalente, pero()
es obsoleto por POSIX{ [ a = a ] || [ a = b ]; } && [ a = b ]
POSIX equivalente 5división de palabras y generación de nombre de archivo en expansiones (split + glob)
x='a b'; [[ $x = 'a b' ]]
: cierto, no se necesitan citasx='a b'; [ $x = 'a b' ]
: error de sintaxis, se expande a[ a b = 'a b' ]
x='*'; [ $x = 'a b' ]
: error de sintaxis si hay más de un archivo en el directorio actual.x='a b'; [ "$x" = 'a b' ]
: Equivalente POSIX=
[[ ab = a? ]]
: cierto, porque hace coincidir patrones (* ? [
son mágicos). No se expande globalmente a los archivos en el directorio actual.[ ab = a? ]
: ela?
globo se expande. Por lo tanto, puede ser verdadero o falso dependiendo de los archivos en el directorio actual.[ ab = a\? ]
: falso, no expansión glob=
y==
son iguales en ambos[
y[[
, pero==
es una extensión Bash.case ab in (a?) echo match; esac
: Equivalente POSIX[[ ab =~ 'ab?' ]]
: falso 4 , pierde magia con''
[[ ab? =~ 'ab?' ]]
: cierto=~
[[ ab =~ ab? ]]
: verdadero, la coincidencia de expresión regular extendida POSIX ,?
no se expande globalmente[ a =~ a ]
: error de sintaxis. No hay bash equivalente.printf 'ab\n' | grep -Eq 'ab?'
: Equivalente POSIX (solo datos de una línea)awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?'
: Equivalente POSIX.Recomendación : usar siempre
[]
.Hay equivalentes POSIX para cada
[[ ]]
construcción que he visto.Si te
[[ ]]
usas:[
es solo un comando regular con un nombre extraño, no hay semántica especial involucrada.¹ Inspirado en la
[[...]]
construcción equivalente en el shell Korn² pero falla para algunos valores de
a
ob
(like+
oindex
) y hace una comparación numérica sia
y seb
parecen a enteros decimales.expr "x$a" '<' "x$b"
trabaja alrededor de ambos.³ y también falla para algunos valores de
a
ob
like!
or(
.4 en bash 3.2 y superior y la compatibilidad proporcionada a bash 3.1 no está habilitada (como con
BASH_COMPAT=3.1
)5 aunque la agrupación (aquí con el
{...;}
grupo de comandos en lugar del(...)
cual se ejecutaría una subshell innecesaria) no es necesaria ya que los operadores de shell||
y&&
(en oposición a los operadores||
y&&
[[...]]
o los operadores-o
/-a
[
) tienen la misma prioridad. Entonces[ a = a ] || [ a = b ] && [ a = b ]
sería equivalente.fuente
expr
a la respuesta. El término "extensión de Bash" no implica que Bash fue el primer intérprete que agregó algo de sintaxis, aprender POSIX sh vs Bash ya es suficiente para volverme loco.man test
si lo intentasteman [
y te perdiste. Eso explicará la variante POSIX.Algunos ejemplos:
Prueba tradicional:
test
y[
son comandos como cualquier otro, por lo que la variable se divide en palabras a menos que esté entre comillas.Prueba de nuevo estilo
[[ ... ]]
es una construcción de shell especial (más reciente), que funciona de manera un poco diferente, lo más obvio es que no tiene variables de división de palabras:Alguna documentación sobre
[
y[[
aquí .Prueba aritmética:
Comandos "normales":
Todo lo anterior actúa como comandos normales y
if
puede tomar cualquier comando:Múltiples comandos:
O podemos usar múltiples comandos. Al envolver un conjunto de comandos, los
( ... )
ejecuta en subshell, creando una copia temporal del estado del shell (directorio de trabajo, variables). Si necesitamos ejecutar algún programa temporalmente en otro directorio:fuente
Comandos de agrupamiento
( list )
Al colocar una lista de comandos entre paréntesis, se crea un entorno de subshell y cada uno de los comandos de la lista se ejecuta en esa subshell. Dado que la lista se ejecuta en una subshell, las asignaciones de variables no permanecen vigentes una vez que se completa la subshell.{ list; }
Colocar una lista de comandos entre llaves hace que la lista se ejecute en el contexto actual del shell . No se crea ninguna subshell. Se requiere la siguiente lista de punto y coma (o nueva línea). FuenteConstrucciones condicionales
Soporte simple, es decir,
[]
para comparación
==, !=, <,
y>
debe usarse y para comparación numéricaeq, ne,lt
ygt
debe usarse.Brackets mejorados, es decir
[[]]
En todos los ejemplos anteriores, utilizamos solo corchetes para encerrar la expresión condicional, pero bash permite corchetes dobles que sirve como una versión mejorada de la sintaxis de un solo corchete.
A modo de comparación
==, !=, <,
y>
puede usar literalmente.[
es sinónimo de comando de prueba. Incluso si está integrado en el shell, crea un nuevo proceso.[[
es una nueva versión mejorada de la misma, que es una palabra clave, no un programa.[[
se entiende porKorn
yBash
.Fuente
fuente