Soy nuevo en los scripts de shell y estoy tratando de aprender los conceptos básicos. Estoy iniciando un script usando la terminal como "./scriptgoeshere.sh Argumento1 Argumento2".
Quiero que el script imprima algo de eco, "Hola mundo" es probablemente el mejor para usar, pero solo quiero que imprima hola mundo si hay: Exactamente dos argumentos, y esos argumentos deben consistir únicamente en números. Por ejemplo:
./scriptgoeshere.sh 1523 9643 ./scriptgoeshere.sh 7 96 Sin embargo, debería funcionar; ./scriptgoeshere.sh 594 arg NO debería funcionar, este es el script que tengo en este momento pero no funciona. He intentado resolver esto todo el día pero parece que no puedo lograrlo, incluso lo intenté con if/elif/else. donde hice que verificara si hay 2 entradas en la parte if y si ambas son números en elif antes de continuar.
#!/bin/bash
if [ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ];
then
i=0
while [ $i -lt 3 ]
do
echo "Hello World!"
sleep 5
i=$((i+1))
done
else
echo "Need two arguments and only numbers will be accepted, such as ./scriptgoeshere 153 251"
fi
Bloquea el bucle de hola mundo hasta que pongo 2 argumentos, pero no importa si son números o texto, y luego aparece un error: "./scriptgoeshere.sh: línea 2: [: ^[0-9]: expresión entera esperada". Recibo ese error incluso si escribo ./scriptgoeshere.sh 123 123 en la terminal para iniciarlo. Prefiero no resolverlo mediante variables porque quiero descubrir los argumentos al iniciar el script real.
¡Esto me está volviendo loca!
Esto es con lo que comencé para verificar si hay exactamente 2 argumentos y funciona, solo quiero agregarle una pequeña solución para que esos 2 argumentos TIENEN que ser números. No entiendo por qué parece tan difícil.
#!/bin/bash
if [ "$#" -eq 2 ];
then
i=0
while [ $i -lt 3 ]
do
echo "Hello World!"
sleep 1
i=$((i+1))
done
else
echo "Need two argument, only numbers accepted"
fi
Editar: lo resolví. Había trabajado muy de cerca... Solo necesitaba agregar [] extra a la parte numérica de la cadena if. ¿Alguien sabe por qué es necesario escribirlo como [[ "$@" -eq ^[0-9] ]] mientras que la primera parte se puede escribir con solo un []?
Respuesta1
Al principio [ "$#" -eq ^[0-9] ]
o [ "$@" -eq ^[0-9] ]
(la pregunta fue editada mientras redactaba mi respuesta…). Por el contexto, creo que quieres probar si el parámetro es un número.
$#
es siempre un número, el número de parámetros posicionales en decimal. Quiere probar dos parámetros posicionales:$1
y$2
.[
es un comando ([ whatever ]
es equivalente atest whatever
). Puede que sea un shell incorporado, pero sintácticamente es comols
oman
. Las palabras que siguen (incluyendo]
) son analizadas por el shell como cualquier palabra que sigue a un comando: el shell las expande (por ejemplo,$#
se convierte en un número), elimina las comillas; eventualmente se convierten en parámetros para[
. Una de estas expansiones es la expansión de nombres de archivos, donde se comparan varios patrones con nombres de archivos existentes (expansión de nombres de archivos, globbing). Sin comillas^[0-9]
puede expandirse^2 ^5
si estos archivos existen. Si no hay coincidencia o si desactiva la función,^[0-9]
llegará[
(como uno de sus argumentos) literalmente. Parece un patrón pero[
de todos modos no admite la coincidencia de patrones.-eq
requiere que el argumento anterior y el siguiente sean números enteros.$#
se expande a un número entero con seguridad. No hay forma de^[0-9]
expandirlo a un número en este contexto.Las comillas dobles
$@
son especiales, pueden expandirse a varias palabras ("campos" en términos de POSIX) a pesar de estar entre comillas. Por lo general, citamos para evitar la división de palabras:"$foo"
se expande a una palabra incluso si el valor contiene espacios y demás."$@"
se expande como"$1" "$2" …
, sus comillas dobles impiden$1
expandirse a varias palabras (y por separado$2
, etc.). En este fragmento:[ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ]
el segundo
[
se ejecuta si el primero tiene éxito (así es como&&
funciona), es decir, si hay exactamente dos parámetros posicionales. En tal caso"$@"
es equivalente a"$1" "$2"
y la segunda prueba se convierte en[ "$1" "$2" -eq ^[0-9] ]
Esto no tiene sentido.
[
Puede quejarse de diferentes maneras dependiendo de los valores reales de los parámetros y la expansión de^[0-9]
, aún así es basura.
Ahora sobre [[ "$#" = [0-9] ]]
.
Nota: este fragmento estaba en la primera revisión de la pregunta. La pregunta fue editada mientras redactaba mi respuesta. Decidí conservar este fragmento porque [[
de todos modos puede resultar útil eventualmente.
- Nuevamente:
$#
es siempre un número. [[
no es equivalente a[
. Cualquier prueba sensata con la que puedas hacer[
puedes reescribirla[[
(aunque no debes hacerlo sin pensar simplemente reemplazándola[
con[[
). Hay pruebas adicionales[[
que se pueden hacer, es más potente. Si bien POSIX requiere[
(y ), no lo es.test
[[
Cosa importante:
[[
no es un comando comols
o[
. Es una palabra clave, cambia la forma en que el shell analiza las palabras siguientes. Todo lo que está entre[[
y]]
se analiza de manera diferente.Si el operador es
=
, el siguiente argumento es un patrón. Algunos caracteres/sintaxis (por ejemplo,*
o[0-9]
), cuando no están entre comillas, se comparan con el argumento anterior a=
. Es como la expansión de nombres de archivos, pero no contra nombres de archivos existentes. Su código probado si (ampliado)$#
tiene exactamente un dígito. Toda la prueba fue[[ "$#" -eq 2 ]] && [[ "$#" = [0-9] ]]
foo && bar
se ejecutabar
sifoo
tiene éxito. El segundo[[
se probó solo si$#
se expandió a2
, por lo que tuvo que tener éxito porque2
tiene exactamente un dígito. En este caso el segundo[[
no cambia nada.
Digamos que te diste cuenta de tu error y probaste si $1
es un número.
- Como arriba,
[[ "$1" = [0-9] ]]
devuelve el éxito si$1
se expande a exactamente un dígito. [[ "$1" = [0-9][0-9] ]]
– exactamente dos dígitos.[[ "$1" = [0-9]* ]]
– algo que comienza con un dígito.
Para detectar cualquier número de dígitos que necesite =~
. Este operador interno [[ ]]
es algo similar a =
, pero ahora el patrón es una expresión regular extendida. Sin embargo, no es necesario que coincida con toda la cadena a la izquierda del operador, una subcadena es suficiente (por lo tanto ^
, $
se necesitan anclajes en su caso):
[[ "$1" =~ [0-9] ]]
devuelve éxito si$1
se expande a una cadena que contiene un dígito.[[ "$1" =~ [0-9][0-9] ]]
– … que contiene dos dígitos uno al lado del otro.[[ "$1" =~ ^[0-9]$ ]]
– una cadena que tiene exactamente un dígito.[[ "$1" =~ ^[0-9]*$ ]]
– cero o más dígitos.[[ "$1" =~ ^[0-9][0-9]*$ ]]
– uno o más dígitos.[[ "$1" =~ ^[0-9]+$ ]]
– uno o más dígitos.
[[ "$1" =~ ^[0-9]+$ ]]
y [[ "$2" =~ ^[0-9]+$ ]]
son las pruebas que quizás desees.Para permitir el liderazgo -
modifíquelos así: [[ "$1" =~ ^-?[0-9]+$ ]]
. Todo el fragmento de prueba puede ser
[ "$#" -eq 2 ] && `[[ "$1" =~ ^-?[0-9]+$ ]]` && `[[ "$2" =~ ^-?[0-9]+$ ]]`
Las variables/parámetros internos [[ ]]
no pueden estar entre comillas dobles y sus valores expandidos no se dividirán ni se agruparán. Entonces puedes escribir[[ $1 =~ ^-?[0-9]+$ ]]
y sea seguro. Es una excepción; en general, las variables deben estar entre comillas dobles. Aquí las comillas dobles no molestan nada, por lo que aconsejo citar de todos modos para acostumbrarse a esta buena práctica general.
Dices que resolviste el problema usando[[ "$@" -eq ^[0-9] ]]
. Lo siento, no te creo. Si hay exactamente dos parámetros posicionales y son números, no veo una manera de que esto pueda ser sintácticamente correcto, y mucho menos probar lo que desea. En otros casos… todavía no veo la manera.
… usando [[ "$@" =~ ^[0-9] ]]
(de tu comentario, incoherente con la pregunta). Ésta no es una solución adecuada. Mis pruebas indican que "$@"
el interior [[ ]]
se comporta como si no estuviera entrecomillado, $@
tratado como una palabra sin división ni agrupación de palabras. Esto es muy similar a cómo se comportan las variables regulares dentro[[ ]]
, aunque no fue obvio para mí de antemano, porque sé que "$@"
no es como las variables regulares.
Nota al margen: por esta razón nunca he querido usar"$@"
en el interior [[ ]]
. No puedo imaginar un escenario en el que sea lo correcto.
Cuando hay exactamente dos parámetros posicionales, su "solución" se convierte en [[ "$1 $2" =~ ^[0-9] ]]
. Es sintácticamente válido y comprueba si el primer carácter del primer argumento de su script es un dígito. Solo esto. Correr./scriptgoeshere.sh 0h well
, los argumentos pasarán la prueba.
Notas:
[0-9]
(como patrón global o expresión regular) depende de su ubicación actual. Lo más probable es que funcione como se espera, pero técnicamente es posible tener una ubicación anterior9
o0
posterior2
.0-9
. La forma más general de hacer coincidir un dígito es[[:digit:]]
. Vereste.Por otro lado, es posible que desee utilizar
$1
y/o$2
con aritmética de shell ($((…))
),[ "$1" -ge 50 ]
o algo así. Entonces solo necesitas números arábigos. Incluso si[[:digit:]]
los incluye en su localidad (probablemente los incluya), también puede incluir otros dígitos. Considerar[0123456789]
; es explícito y no depende de la configuración regional.No todas las cadenas que coinciden
^-?[0-9]+$
se consideran enteras desde el punto de vista de[
y/o[[
(por ejemplo, cuando lo hace[ "$1" -eq 0 ]
). Cada herramienta tiene un rango de números enteros que puede manejar. De manera similar, la aritmética del shell puede producir resultados matemáticamente incorrectos si la alimenta con una cadena de dígitos que describe un número entero fuera de rango.Matemáticamente
1.00
es un número entero. Algunas herramientas pueden aceptarlo como un número entero, otras no. Qué pasa1e100
?