Variable con comillas "$ ()"

12

Escribí este script:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

Soy un novato tratando de aprender rápido y tengo una pregunta sobre las comillas de la variable .

Ponga una variable entre $ (), lo entiendo, pero ¿por qué se necesitan las comillas incluso en la ifdeclaración? ¿Es para hacer un comando anidado?

Shankhara
fuente
55
Tenga en cuenta que while [ true ]produce un bucle infinito, pero tal vez no por la razón que cree que lo hace; while [ false ] también produce un bucle infinito, porque con un solo argumento, [ ... ]tiene éxito si ese argumento es una cadena no vacía. while trueen realidad ejecutará un comando llamado true(que siempre tiene éxito).
chepner
44
No pones una variable dentro $(). Pones un comando dentro $().
Comodín el

Respuestas:

22

Las comillas evitan la "división de palabras". Es decir: desglosar las variables en varios elementos en los espacios en blanco (o para ser más exactos, en espacios, tabulaciones y líneas nuevas como se define en el valor de la $IFSvariable de shell predeterminada ).

Por ejemplo,

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

Aquí definimos la howmanyfunción que simplemente nos permite saber cuántos parámetros posicionales se dan. Como puede ver, hay dos elementos que se pasan a la variable, y con las comillas, el texto en la variable se trata como una unidad.

Esto es importante para el paso preciso de la información. Por ejemplo, si la variable contiene la ruta al archivo y el nombre del archivo contiene espacios en cualquier parte de la ruta, el comando que está intentando ejecutar puede fallar o dar resultados inexactos. Si estuviéramos intentando crear un archivo con la $varvariable, touch $varcrearíamos dos archivos, pero touch "$var"solo uno.

Lo mismo va para tu [ "$currentoutput" != "$lastoutput" ]parte. Esta prueba particular realiza una comparación en dos cadenas. Cuando se ejecuta la prueba, el [comando necesitaría ver 3 argumentos: una cadena de texto, el !=operador y otra cadena de texto. Mantener comillas dobles evita la división de palabras, y el [comando ve exactamente esos 3 argumentos. ¿Qué sucede si las variables no están entre comillas?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

Aquí, la división de palabras, y en su lugar [ve a dos cadenas helloy worldseguido por !=, seguidas de otras dos cadenas hi world. El punto clave es que sin comillas dobles, el contenido de las variables se entiende como unidades separadas en lugar de un elemento completo.

Asignar la sustitución de comandos no requiere comillas dobles como en

var=$( df )

donde tiene dfguardada la salida del comando var. Sin embargo, es una buena costumbre siempre duplicar las variables de comillas y la sustitución de comandos a $(...)menos que de hecho desee que la salida se trate como elementos separados.


En una nota al margen, el

while [ true ]

parte puede ser

while true

[es un comando que evalúa sus argumentos y [ whatever ]siempre es verdadero independientemente de lo que haya dentro. Por el contrario, while trueusa el comando trueque siempre devuelve el estado de salida de éxito (y eso es exactamente lo que whilenecesita el bucle). La diferencia es un poco más de claridad y menos pruebas realizadas. Alternativamente, también podría usar en :lugar detrue

Las comillas dobles en echo "" date and Timeparte probablemente podrían eliminarse. Simplemente insertan una cadena vacía y agregan espacio extra a la salida. Si eso es lo que desea, siéntase libre de mantenerlos allí, pero no hay un valor funcional particular en este caso.

lsusb >> test.log

Esta parte probablemente podría ser reemplazada por echo "$currentoutput" >> test.log. No hay razón para lsusbvolver a ejecutar después de que ya se haya ejecutado currentoutput=$(lsusb). En los casos en que las nuevas líneas finales deben conservarse en la salida, se podría ver el valor de ejecutar un comando varias veces, pero en caso de lsusbque no sea necesario. Cuantos menos comandos externos llame, mejor, porque cada llamada a un comando no incorporado conlleva costos en CPU, uso de memoria y tiempo de ejecución (aunque los comandos probablemente estén precargados desde la memoria).


Ver también:

Sergiy Kolodyazhnyy
fuente
1
Gran respuesta, si aún no lo hiciste, deberías escribir un libro sobre bash. Pero en la última parte no debería ser echo "$currentoutput" >> test.log mejor printf '%s\n' "$currentoutput" >> test.log ?
pLumo
2
@RoVo Sí, printfdebería preferirse por su portabilidad. Como estamos usando un script específico de bash aquí, se puede excusar su uso echo. Pero su observación es muy correcta
Sergiy Kolodyazhnyy
1
@SergiyKolodyazhnyy, ... No estoy seguro de que echoes completamente fiable incluso cuando el depósito es conocido por ser fiesta (y el valor de los xpg_echoy posixlas banderas se conoce); si su valor podría ser -no -e, puede desaparecer en lugar de imprimirse.
Charles Duffy el
4

En currentoutput="$(lsusb)"lsusb no es una variable, es un comando. Lo que hace esta declaración, ejecuta el lsusbcomando y asigna su salida a la currentoutputvariable.

La sintaxis anterior para esto era

currentoutput=`lsusb`

puedes encontrarlo en muchos ejemplos y scripts

Para responder a la otra parte de su pregunta, if [ ]es cómo ifse define la sintaxis para bash. Ver más en https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html

marosg
fuente
3
Creo que es importante decir que en [ ]realidad es el testcomando. También puede usar ifdeclaraciones con otros comandos, ya que se basa en una prueba 0 o no cero de su código de salida.
Arronical
... de hecho, es mucho mejor correr if grep -qe "somestring" fileque correr grep -qe "somestring" file; if [ $? = 0 ]; then ..., por lo que la afirmación que if [ ...forma parte de la definición de ifsintaxis no solo es engañosa sino que conduce a malas prácticas.
Charles Duffy el
4

Lo siguiente ejecuta el comando externo commandy devuelve su salida.

"$(command)"

Sin los corchetes / paréntesis, esto buscaría una variable en lugar de ejecutar un comando:

"$variable"

En cuanto a la diferencia entre $variabley "$variable", esto se vuelve relevante cuando $variablecontiene espacios. Cuando se usa "$variable", todo el contenido de la variable se insertará en una sola cadena, incluso si el contenido incluye espacios. Cuando se utiliza $variableel contenido de la variable puede expandirse en una lista de argumentos de múltiples argumentos.

thomasrutter
fuente
Hola @thomasrutter, lo siento, quise decir comillas ... ¡Edito mi comentario ahora!
Shankhara
0

Para ser contraria, bash recomienda que use [[ ... ]]over the [ ... ]construct para evitar tener que citar variables en la prueba y, por lo tanto, los problemas asociados de división de palabras que los otros han señalado.

[se proporciona en bash para la compatibilidad POSIX con scripts destinados a ejecutarse #!/bin/sho aquellos que se transfieren a bash; en su mayor parte, debe evitarlo a favor [[.

p.ej

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal
shalomb
fuente
bashno hace tal recomendación; que proporciona [[ ... ]] lo que puede ser más conveniente y tiene algunas funciones que [ ... ]no lo hace, pero no hay ningún problema con el uso [ ... ] correctamente .
chepner
@chepner - Quizás debería ser más claro aquí. Claro que no es una recomendación explícita en la página de manual de bash, pero dado [[hace todo lo que [hace y aún más, y muchas veces hace [tropezar a las personas y luego intenta adaptar las características de [[atrás [para fallar y dejar los errores dispersos: es justo decir que es un recomendación de la comunidad en la medida en que las guías de estilo bash más relevantes aconsejen nunca usar[ .
shalomb