Como comentario aparte, el test && echo "foo" && exit 0 || echo "bar" && exit 1enfoque que está utilizando puede tener algunos efectos secundarios no deseados: si el eco falla (tal vez la salida es a un FD cerrado), exit 0se omitirá y el código lo intentará echo "bar". Si falla en eso también, la &&condición fallará, ¡y ni siquiera se ejecutará exit 1! El uso de ifdeclaraciones reales en lugar de &&/ ||es menos propenso a efectos secundarios inesperados.
Charles Duffy el
@CharlesDuffy ¡Ese es el tipo de pensamiento realmente inteligente al que la mayoría de las personas solo llegan cuando tienen que localizar bichos peludos ...! Nunca pensé que el eco podría devolver el fracaso.
Camilo Martin
66
Llegué un poco tarde a la fiesta, pero sé sobre los peligros sobre los que Charles escribió, ya que tuve que pasar por ellos hace bastante tiempo también. Así que aquí hay una línea 100% infalible (y bien legible) para usted: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }los corchetes indican que las cosas NO deben ejecutarse en una subshell (que definitivamente sería así con ()paréntesis en su lugar). Advertencia: nunca te pierdas el punto y coma final . De lo contrario, podría bashimprimir los mensajes de error más feos (y sin sentido) ...
syntaxerror
55
No funciona en Ubuntu, a menos que no elimine las comillas. Así que debería ser[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Treviño
44
Deberá ser más específico sobre lo que quiere decir con "número" . Un entero? ¿Un número de punto fijo? Notación científica ("e")? ¿Existe un rango requerido (por ejemplo, un valor sin signo de 64 bits) o permite algún número que pueda escribirse?
Toby Speight
Respuestas:
803
Un enfoque es usar una expresión regular, así:
re='^[0-9]+$'if![[ $yournumber =~ $re ]];then
echo "error: Not a number">&2; exit 1fi
Si el valor no es necesariamente un número entero, considere enmendar la expresión regular adecuadamente; por ejemplo:
+1 para este enfoque, pero tenga cuidado con los decimales, haciendo esta prueba con, por ejemplo, "1.0" o "1.0" imprime el error "No es un número".
sourcerebels
15
Encuentro el '' exec> & 2; echo ... '' bastante tonto. Sólo '' eco ...> & 2 ''
lhunath
55
@Ben, ¿realmente quieres manejar más de un signo menos? Lo haría en ^-?lugar de a ^-*menos que realmente estés haciendo el trabajo para manejar múltiples inversiones correctamente.
Charles Duffy
44
@SandraSchlichting Hace que toda la producción futura vaya a stderr. No es realmente un punto aquí, donde solo hay un eco, pero es un hábito en el que tiendo a incursionar en los casos en que los mensajes de error abarcan varias líneas.
Charles Duffy
29
No estoy seguro de por qué la expresión regular debe guardarse en una variable, pero si es por razones de compatibilidad, no creo que sea necesario. Se podía aplicar directamente la expresión: [[ $yournumber =~ ^[0-9]+$ ]].
konsolebox
284
Sin bashisms (funciona incluso en el Sistema V sh),
case $string in''|*[!0-9]*) echo bad ;;*) echo good ;;esac
Esto rechaza cadenas vacías y cadenas que no contienen dígitos, aceptando todo lo demás.
Los números negativos o de coma flotante necesitan un trabajo adicional. Una idea es excluir -/ .en el primer patrón "malo" y agregar más patrones "malos" que contengan los usos inapropiados de ellos ( ?*-*/ *.*.*)
+1: es una forma idiomática y portátil de regreso al shell Bourne original, y tiene soporte incorporado para comodines de estilo glob. Si vienes de otro lenguaje de programación, parece extraño, pero es mucho más elegante que lidiar con la fragilidad de varios problemas de if test ...
citas
66
Puede cambiar la primera línea a ${string#-}(que no funciona en conchas Bourne antiguas, pero funciona en cualquier shell POSIX) para aceptar enteros negativos.
Gilles 'SO- deja de ser malvado'
44
Además, esto es fácil de extender a los flotadores: solo agregue '.' | *.*.*a los patrones no permitidos y agregue puntos a los caracteres permitidos. Del mismo modo, puede permitir un signo opcional antes, aunque entonces preferiría case ${string#[-+]}simplemente ignorar el signo.
@Dor Las comillas no son necesarias, ya que el comando mayúsculas y minúsculas no realiza la división de palabras y la generación de nombres de ruta en esa palabra de todos modos. (Sin embargo, las expansiones en los patrones de casos pueden necesitar citando ya que determina si los caracteres de coincidencia de patrones son literales o especial.)
Jilles
193
La siguiente solución también se puede usar en shells básicos como Bourne sin la necesidad de expresiones regulares. Básicamente, cualquier operación de evaluación de valor numérico que utilice números que no sean números dará como resultado un error que se considerará implícitamente como falso en el shell:
"$var"-eq "$var"
como en:
#!/bin/bash
var=a
if[-n "$var"]&&["$var"-eq "$var"]2>/dev/null;then
echo number
else
echo not a number
fi
También puedes probar por $? El código de retorno de la operación que es más explícito:
[-n "$var"]&&["$var"-eq "$var"]2>/dev/null
if[ $?-ne 0];then
echo $var is not number
fi
La redirección del error estándar está ahí para ocultar el mensaje de "expresión entera esperada" que bash imprime en caso de que no tengamos un número.
CUEVAS (gracias a los comentarios a continuación):
Los números con puntos decimales no se identifican como "números" válidos
Usar en [[ ]]lugar de [ ]siempre evaluará atrue
La mayoría de los shells que no son Bash siempre evaluarán esta expresión como true
El comportamiento en Bash no está documentado y, por lo tanto, puede cambiar sin previo aviso.
Si el valor incluye espacios después del número (por ejemplo, "1 a") produce un error, como bash: [[: 1 a: syntax error in expression (error token is "a")
Si el valor es el mismo que el nombre var (por ejemplo, i = "i"), produce un error, como bash: [[: i: expression recursion level exceeded (error token is "i")
Todavía lo recomendaría (pero con las variables citadas para permitir cadenas vacías), ya que el resultado está garantizado para ser utilizado como un número en Bash, pase lo que pase.
l0b0
21
Tenga cuidado de usar corchetes individuales; [[ a -eq a ]]se evalúa como verdadero (ambos argumentos se convierten a cero)
Tgr
3
¡Muy agradable! Tenga en cuenta que esto solo funciona para un número entero, no para cualquier número. Necesitaba verificar un solo argumento que debe ser un número entero, por lo que funcionó bien:if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
haridsv
66
Aconsejaría encarecidamente este método debido al número no insignificante de shells cuyo valor [incorporado evaluará los argumentos como aritméticos. Eso es cierto tanto en ksh93 como en mksh. Además, dado que ambas matrices de soporte, hay una oportunidad fácil para la inyección de código. Use una coincidencia de patrón en su lugar.
ormaaj
3
@AlbertoZaccagni, en las versiones actuales de bash, estos valores se interpretan con reglas de contexto numérico solo para [[ ]]pero no para [ ]. Dicho esto, este comportamiento no está especificado por el estándar POSIX para testy en la propia documentación de bash; versiones futuras de bash podrían modificar el comportamiento para que coincida con ksh sin romper ninguna promesa de comportamiento documentada, por lo que no se garantiza que depender de su comportamiento actual persistente sea seguro.
Charles Duffy
43
Esto prueba si un número es un número entero no negativo y es independiente de la shell (es decir, sin bashisms) y solo utiliza incorporaciones de shell:
INCORRECTO.
Como esta primera respuesta (a continuación) permite enteros con caracteres en ellos, siempre que los primeros no sean los primeros en la variable.
[-z "${num##[0-9]*}"]&& echo "is a number"|| echo "is not a number";
CORRECTO .
Como comentó y sugirió Jilles en su respuesta, esta es la forma correcta de hacerlo utilizando patrones de concha.
[!-z "${num##*[!0-9]*}"]&& echo "is a number"|| echo "is not a number";
Esto no funciona correctamente, acepta cualquier cadena que comience con un dígito. Tenga en cuenta que WORD en $ {VAR ## WORD} y similar es un patrón de shell, no una expresión regular.
jilles
2
¿Puedes traducir esa expresión al inglés, por favor? Realmente quiero usarlo, pero no lo entiendo lo suficiente como para confiar en él, incluso después de leer la página de manual de bash.
CivFan
2
*[!0-9]*es un patrón que coincide con todas las cadenas con al menos 1 carácter sin dígitos. ${num##*[!0-9]*}es una "expansión de parámetros" donde tomamos el contenido de la numvariable y eliminamos la cadena más larga que coincide con el patrón. Si el resultado de la expansión del parámetro no está vacío ( ! [ -z ${...} ]), entonces es un número, ya que no contiene ningún carácter que no sea un dígito.
mrucci
Desafortunadamente, esto falla si hay dígitos en el argumento, incluso si no es un número válido. Por ejemplo "exam1ple" o "a2b".
Glenn, shopt -s extglobelimino de tu publicación (que he votado, es una de mis respuestas favoritas aquí), ya que en Construcciones condicionales puedes leer: Cuando se usan los operadores ==y !=, la cadena a la derecha del operador se considera un patrón y coincide de acuerdo con las reglas descritas a continuación en Pattern Matching , como si la extglobopción de shell estuviera habilitada. ¡Espero que no te moleste!
gniourf_gniourf
En tales contextos, no es necesario shopt extglob... ¡es bueno saberlo!
gniourf_gniourf
Funciona bien para enteros simples.
2
@Jdamian: tienes razón, esto se agregó en Bash 4.1 (que se lanzó a finales de 2009 ... Bash 3.2 se lanzó en 2006 ... ahora es un software antiguo, lo siento por aquellos que están atrapados en el pasado). Además, podría argumentar que extglobs se introdujo en la versión 2.02 (lanzada en 1998), y no funciona en versiones <2.02 ... Ahora su comentario aquí servirá como una advertencia con respecto a las versiones anteriores.
gniourf_gniourf
1
Las variables dentro [[...]]no están sujetas a división de palabras o expansión global.
Glenn Jackman
26
Estoy sorprendido por las soluciones que analizan directamente los formatos de números en shell. Shell no se adapta bien a esto, ya que es un DSL para controlar archivos y procesos. Hay numerosos analizadores numéricos un poco más abajo, por ejemplo:
isdecimal(){# filter octal/hex/ord()
num=$(printf '%s'"$1"| sed "s/^0*\([1-9]\)/\1/; s/'/^/")
test "$num"&& printf '%f'"$num">/dev/null 2>&1}
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
Gilles Quenot
44
@sputnick su versión rompe la semántica de valor de retorno inherente (y útil) de la función original. Por lo tanto, simplemente deje la función como está y úsela:isnumber 23 && echo "this is a number" || echo "not a number"
michael
44
¿No debería tener esto también 2>/dev/null, para que isnumber "foo"no contamine stderr?
gioele
44
Llamar shells modernos como bash "un DSL para controlar archivos y procesos" es ignorar que se usan para mucho más que eso: algunas distribuciones han construido administradores de paquetes completos e interfaces web (por muy feo que sea). Sin embargo, los archivos por lotes se ajustan a su descripción, ya que incluso establecer una variable allí es difícil.
Camilo Martin
77
Es curioso que intentes ser inteligente copiando algunas expresiones idiomáticas de otros idiomas. Lamentablemente, esto no funciona en los depósitos. Los shells son muy especiales, y sin un conocimiento sólido sobre ellos, es probable que escriba código roto. Su código está roto: isnumber "'a"devolverá verdadero. Esto está documentado en la especificación POSIX donde leerá: Si el carácter inicial es una comilla simple o doble, el valor será el valor numérico en el conjunto de códigos subyacente del carácter que sigue a la comilla simple o doble .
gniourf_gniourf
16
Estaba mirando las respuestas y ... ¡me di cuenta de que nadie pensaba en los números FLOAT (con punto)!
Usar grep también es genial.
-E significa
expresión regular extendida
-q significa silencio (no hace eco) -qE es la combinación de ambos.
Las soluciones que utilizan awk de triple_r y tripleee funcionan con flotadores.
Ken Jackson
Gracias por esto y muy buen punto! Porque la pregunta es en realidad cómo verificar si es un número y no solo un número entero.
Tanasis
Yo también te agradezco Tanasis! Ayudémonos unos a otros siempre.
Sergio Abreu
12
Solo un seguimiento a @mary. Pero debido a que no tengo suficiente representante, no pude publicar esto como un comentario a esa publicación. De todos modos, esto es lo que usé:
isnum(){ awk -v a="$1"'BEGIN {print (a == a + 0)}';}
La función devolverá "1" si el argumento es un número, de lo contrario devolverá "0". Esto funciona tanto para enteros como para flotadores. El uso es algo como:
n=-2.05e+07
res=`isnum "$n"`if["$res"=="1"];then
echo "$n is a number"else
echo "$n is not a number"fi
Imprimir un número es menos útil que configurar un código de salida. 'BEGIN { exit(1-(a==a+0)) }'es un poco difícil de asimilar pero se puede utilizar en una función que devuelve verdadero o falso como [, grep -q, etc.
tripleee
9
test -z "${i//[0-9]}"&& echo digits || echo no no no
${i//[0-9]}reemplaza cualquier dígito en el valor de $icon una cadena vacía, ver man -P 'less +/parameter\/' bash. -zcomprueba si la cadena resultante tiene longitud cero.
si también desea excluir el caso cuando $iestá vacío, puede usar una de estas construcciones:
test -n "$i"&& test -z "${i//[0-9]}"&& echo digits || echo not a number
[[-n "$i"&&-z "${i//[0-9]}"]]&& echo digits || echo not a number
Para mi problema, solo necesitaba asegurarme de que un usuario no ingresara accidentalmente algún texto, por lo que traté de mantenerlo simple y legible
isNumber(){(( $1 ))2>/dev/null
}
De acuerdo con la página del manual, esto hace más o menos lo que quiero
Si el valor de la expresión no es cero, el estado de retorno es 0
Para evitar mensajes de error desagradables para cadenas que "podrían ser números", ignoro la salida de error
$ ((2s))
bash:((:2s: value too great for base (error token is "2s")
Esto es más como isInteger. Pero increíble gracias!
Naheel
8
Antigua pregunta, pero solo quería agregar mi solución. Este no requiere ningún truco extraño de shell, ni depende de algo que no ha existido desde siempre.
if[-n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')"];then
echo 'is not numeric'else
echo 'is numeric'fi
Básicamente, solo elimina todos los dígitos de la entrada, y si te queda una cadena de longitud diferente de cero, entonces no era un número.
O para variables con líneas nuevas o algo así $'0\n\n\n1\n\n\n2\n\n\n3\n'.
gniourf_gniourf
7
No puedo comentar aún, así que agregaré mi propia respuesta, que es una extensión de la respuesta de Glenn Jackman usando la coincidencia de patrones bash.
Mi necesidad original era identificar números y distinguir enteros y flotantes. Las definiciones de funciones deducidas a:
function isInteger(){[[ ${1}==?(-)+([0-9])]]}function isFloat(){[[ ${1}==?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9]))]]}
Utilicé pruebas unitarias (con shUnit2) para validar que mis patrones funcionaron según lo previsto:
oneTimeSetUp(){
int_values="0 123 -0 -123"
float_values="0.0 0. .0 -0.0 -0. -.0 \
123.456 123. .456 -123.456 -123. -.456
123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"}
testIsIntegerIsFloat(){local value
for value in ${int_values}do
assertTrue "${value} should be tested as integer""isInteger ${value}"
assertFalse "${value} should not be tested as float""isFloat ${value}"donefor value in ${float_values}do
assertTrue "${value} should be tested as float""isFloat ${value}"
assertFalse "${value} should not be tested as integer""isInteger ${value}"done}
Notas: El patrón isFloat se puede modificar para que sea más tolerante con el punto decimal ( @(.,)) y el símbolo E ( @(Ee)). Las pruebas de mi unidad solo prueban valores que son enteros o flotantes, pero no una entrada no válida.
ya sea duplicado de, o tal vez más adecuado como comentario para, la respuesta de pixelbeat (el uso %fes probablemente mejor de todos modos)
michael
3
En lugar de verificar el código de estado anterior, ¿por qué no simplemente ponerlo en ifsí mismo? Eso es lo ifque ...if printf "%g" "$var" &> /dev/null; then ...
Camilo Martin
3
Esto tiene otras advertencias. Validará la cadena vacía y cadenas como 'a.
gniourf_gniourf
6
@Charles Dufy y otros ya han dado una respuesta clara. Una solución bash pura usaría lo siguiente:
string="-12,345"if[["$string"=~^-?[0-9]+[.,]?[0-9]*$ ]]then
echo $string is a number
else
echo $string is not a number
fi
Aunque para los números reales no es obligatorio tener un número antes del punto radix .
Para proporcionar un soporte más completo de números flotantes y notación científica (muchos programas en C / Fortran o exportarán flotantes de esta manera), una adición útil a esta línea sería la siguiente:
string="1.2345E-67"if[["$string"=~^-?[0-9]*[.,]?[0-9]*[eE]?-?[0-9]+$ ]]then
echo $string is a number
else
echo $string is not a number
fi
Por lo tanto, conduce a una forma de diferenciar los tipos de números, si está buscando algún tipo específico:
string="-12,345"if[["$string"=~^-?[0-9]+$ ]]then
echo $string is an integer
elif[["$string"=~^-?[0-9]*[.,]?[0-9]*$ ]]then
echo $string is a float
elif[["$string"=~^-?[0-9]*[.,]?[0-9]*[eE]-?[0-9]+$ ]]then
echo $string is a scientific number
else
echo $string is not a number
fi
Nota: Podríamos enumerar los requisitos sintácticos para la notación decimal y científica, uno de ellos para permitir la coma como punto de raíz, así como ".". Entonces afirmaríamos que debe haber solo uno de esos puntos radix. Puede haber dos signos +/- en un flotador [Ee]. Aprendí algunas reglas más del trabajo de Aulu, y probé contra cadenas malas como '' '-' '-E-1' '0-0'. Aquí están mis herramientas regex / substring / expr que parecen estar resistiendo:
¿Cuál es la versión mínima de bash? Acabo de recibir bash: operador binario condicional esperado bash: error de sintaxis cerca de token inesperado `= ~ '
Paul Hargreaves
1
@PaulHargreaves =~existió al menos desde Bash 3.0.
Gilles 'SO- deja de ser malvado'
@PaulHargreaves probablemente haya tenido un problema con su primer operando, por ejemplo, demasiadas comillas o similar
Joshua Clayton
@JoshuaClayton Pregunté acerca de la versión porque es una fiesta muy muy antigua en una caja de Solaris 7, que todavía tenemos y no es compatible = ~
Paul Hargreaves
6
Esto se puede lograr usando greppara ver si la variable en cuestión coincide con una expresión regular extendida.
Prueba de entero 1120:
yournumber=1120if echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Salida: Valid number.
Prueba no entera 1120a:
yournumber=1120aif echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Salida: Error: not a number.
Explicación
El grep, el -Einterruptor nos permite usar expresiones regulares extendidas '^[0-9]+$'. Esta expresión regular significa que la variable solo debe []contener los números del 0-9cero al nueve desde el ^principio hasta el $final de la variable y debe tener al menos +un carácter.
El grep, el -qinterruptor de silencio se apaga cualquier salida si es o no encuentra nada.
ifcomprueba el estado de salida de grep. El estado de salida 0significa éxito y cualquier cosa mayor significa un error. El grepcomando tiene un estado de salida de 0si encuentra una coincidencia y 1cuándo no;
Entonces, al ponerlo todo junto, en la ifprueba, hacemos echola variable $yournumbery la |canalizamos a la grepque con el -qinterruptor coincide silenciosamente con la -Eexpresión de '^[0-9]+$'expresión regular extendida . El estado de salida de grepserá 0si grepse encuentra con éxito una coincidencia y 1si no. Si lo conseguimos, nosotros echo "Valid number.". Si no coincide, nosotros echo "Error: not a number.".
Para carrozas o dobles
Simplemente podemos cambiar la expresión regular de '^[0-9]+$'a '^[0-9]*\.?[0-9]+$'para flotantes o dobles.
Prueba de flotación 1120.01:
yournumber=1120.01if echo "$yournumber"| grep -qE '^[0-9]*\.?[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Salida: Valid number.
Prueba de flotación 11.20.01:
yournumber=11.20.01if echo "$yournumber"| grep -qE '^[0-9]*\.?[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Salida: Error: not a number.
Para negativos
Para permitir enteros negativos, simplemente cambie la expresión regular de '^[0-9]+$'a '^\-?[0-9]+$'.
Para permitir flotantes o dobles negativos, simplemente cambie la expresión regular de '^[0-9]*\.?[0-9]+$'a '^\-?[0-9]*\.?[0-9]+$'.
Tienes razón, @CharlesDuffy. Gracias. Limpié la respuesta.
Joseph Shih
Correcto, tienes razón, @CharlesDuffy. ¡Fijo!
Joseph Shih
1
LGTM; La respuesta como editada tiene mi +1. Las únicas cosas que haría de manera diferente en este punto son solo cuestiones de opinión en lugar de corrección (f / e, usar en [-]lugar de \-y en [.]lugar de \.es un poco más detallado, pero significa que sus cadenas no tienen que cambiar si ' Reutilizado en un contexto donde las barras invertidas se consumen).
También puedes usar las clases de personajes de bash.
if[[ $VAR =*[[:digit:]]*]];then
echo "$VAR is numeric"else
echo "$VAR is not numeric"fi
Los números incluirán espacio, el punto decimal y "e" o "E" para coma flotante.
Pero, si especifica un número hexadecimal de estilo C, es decir, "0xffff" o "0XFFFF", [[: dígito:]] devuelve verdadero. Un poco de trampa aquí, bash te permite hacer algo como "0xAZ00" y aún contarlo como un dígito (¿no es esto de alguna peculiaridad extraña de los compiladores de GCC que te permiten usar la notación 0x para bases distintas de 16 ??? )
Es posible que desee probar "0x" o "0X" antes de probar si es un valor numérico si su entrada no es de confianza, a menos que desee aceptar números hexadecimales. Eso se lograría mediante:
if[[ ${VARIABLE:1:2}="0x"]]||[[ ${VARIABLE:1:2}="0X"]];then echo "$VAR is not numeric";fi
[[ $VAR = *[[:digit:]]* ]]devolverá verdadero si la variable contiene un número, no si es un número entero.
Glenn Jackman
[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"impresiones numeric. Probado en la versión bash 3.2.25(1)-release.
Jdamian
1
@ultraswadable, su solución detecta aquellas cadenas que contienen, al menos, un dígito rodeado (o no) por cualquier otro carácter. Yo voté en contra.
Jdamian
El enfoque obviamente correcto es, por lo tanto, revertir esto, y usar[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
eschwartz
@eschwartz, su solución no funciona con números negativos
Angel
4
La forma más simple es verificar si contiene caracteres que no sean dígitos. Reemplaza todos los caracteres de dígitos con nada y verifica la longitud. Si hay longitud, no es un número.
if[[!-n ${input//[0-9]/}]];then
echo "Input Is A Number"fi
Manejar números negativos requeriría un enfoque más complicado.
Andy
... O un signo positivo opcional.
tripleee
@tripleee, me gustaría ver tu enfoque si sabes cómo hacerlo.
Andy
4
Como tuve que manipular esto últimamente y me gusta enfoque de karttu con la prueba de la unidad más. Revisé el código y agregué algunas otras soluciones también, pruébelo usted mismo para ver los resultados:
Entonces isNumber () incluye guiones, comas y notación exponencial y, por lo tanto, devuelve TRUE en enteros y flotantes, mientras que isFloat () devuelve FALSE en valores enteros y isInteger () también devuelve FALSE en flotantes. Para su conveniencia, todo en uno:
Personalmente, eliminaría la functionpalabra clave ya que no hace nada útil. Además, no estoy seguro de la utilidad de los valores de retorno. A menos que se especifique lo contrario, las funciones devolverán el estado de salida del último comando, por lo que no necesita hacer returnnada usted mismo.
Tom Fenech
Agradable, de hecho, los returns son confusos y lo hacen menos legible. Usar functionpalabras clave o no es más una cuestión de sabor personal, al menos las eliminé de los revestimientos para ahorrar espacio. gracias.
3ronco
No olvide que se necesitan punto y coma después de las pruebas para las versiones de una línea.
Tom Fenech
2
isNumber devolverá 'verdadero' en cualquier cadena que tenga un número.
DrStrangepork
@DrStrangepork De hecho, mi matriz false_values no tiene ese caso. Tendré que investigarlo. Gracias por la pista.
3ronco
4
Yo uso expr . Devuelve un valor distinto de cero si intenta agregar un cero a un valor no numérico:
if expr --"$number"+0>/dev/null 2>&1then
echo "$number is a number"else
echo "$number isn't a number"fi
Podría ser posible usar bc si necesita números no enteros, pero no creo que bctenga el mismo comportamiento. Agregar cero a un no número te da cero y también devuelve un valor de cero. Quizás puedas combinar bcy expr. Use bcpara agregar cero a $number. Si la respuesta es 0, intente exprverificar que $numberno sea cero.
Esto es bastante malo. Para hacerlo un poco mejor debes usar expr -- "$number" + 0; sin embargo, esto seguirá pretendiendo eso 0 isn't a number. De man expr:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
gniourf_gniourf
3
Me gusta la respuesta de Alberto Zaccagni.
if["$var"-eq "$var"]2>/dev/null;then
Requisitos previos importantes: - no se generan subcapas - no se invocan analizadores RE - la mayoría de las aplicaciones de shell no usan números reales
Pero si $vares complejo (por ejemplo, un acceso de matriz asociativo), y si el número será un número entero no negativo (la mayoría de los casos de uso), ¿entonces esto es quizás más eficiente?
Utilizo printf como se menciona en otras respuestas, si proporciona la cadena de formato "% f" o "% i" printf hará la comprobación por usted. Más fácil que reinventar los cheques, la sintaxis es simple y corta y printf es omnipresente. Por lo tanto, es una opción decente en mi opinión: también puede usar la siguiente idea para verificar una variedad de cosas, no solo es útil para verificar números.
declare -r CHECK_FLOAT="%f"
declare -r CHECK_INTEGER="%i"## <arg 1> Number - Number to check ## <arg 2> String - Number type to check ## <arg 3> String - Error message function check_number(){local NUMBER="${1}"local NUMBER_TYPE="${2}"local ERROR_MESG="${3}"local-i PASS=1local-i FAIL=0case"${NUMBER_TYPE}"in"${CHECK_FLOAT}")if((! $(printf "${CHECK_FLOAT}""${NUMBER}"&>/dev/random;echo $?)));then
echo "${PASS}"else
echo "${ERROR_MESG}"1>&2
echo "${FAIL}"fi;;"${CHECK_INTEGER}")if((! $(printf "${CHECK_INTEGER}""${NUMBER}"&>/dev/random;echo $?)));then
echo "${PASS}"else
echo "${ERROR_MESG}"1>&2
echo "${FAIL}"fi;;*)
echo "Invalid number type format: ${NUMBER_TYPE} to check_number()."1>&2
echo "${FAIL}";;esac}
>$ var=45
>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }
Como la respuesta de Charles Duffy funciona, pero solo usagolpetazoexpresión regular , y sé que esto es lento, me gustaría mostrar otra forma, usando sologolpetazoexpansión de parámetros :
set--"foo bar"
is_num "$1"&& VAR=$1 || echo "need a number"
need a number
set--"+3.141592"
is_num "$1"&& VAR=$1 || echo "need a number"
echo $VAR
+3.141592
if is_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fiIt's a number
if cdIs_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if is_num 3+4;then echo It\'s a number ;else echo Not a number;fiNot a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fiIt's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
OK eso está bien. Ahora, ¿cuánto tiempo llevará todo esto (en mi frambuesa pi):
time for i in{1..1000};do is_num +3.14159265;done
real 0m2.476s
user 0m1.235s
sys 0m0.000s
Luego con expresiones regulares:
time for i in{1..1000};do cdIs_num +3.14159265;done
real 0m4.363s
user 0m2.168s
sys 0m0.000s
Estoy de acuerdo, de todos modos, prefiero no usar expresiones regulares, cuando podría usar la expansión de parámetros ... El abuso de RE hará que el script bash sea más lento
F. Hauri
Respuesta editada: ¡No es necesario extglob(y un poco más rápido)!
F. Hauri
@CharlesDuffy, en mi frambuesa pi, ¡1000 iteraciones tomarán 2,5 segundos con mi versión y 4,4 segundos con tu versión!
F. Hauri
No puedo discutir con eso. 👍
Charles Duffy
1
Para atrapar números negativos:
if[[ $1 ==?(-)+([0-9.])]]then
echo number
else
echo not a number
fi
Además, esto requiere un globbing extendido para habilitarse primero. Esta es una característica de solo Bash que está deshabilitada de manera predeterminada.
tripleee
@tripleee Extended Globbing se activa automáticamente cuando se usa == o! = When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled.gnu.org/software/bash/manual/bashref.html#index-_005b_005b
Badr Elmers
@BadrElmers Gracias por la actualización. Este parece ser un nuevo comportamiento que no es cierto en mi Bash 3.2.57 (MacOS Mojave). Veo que funciona como lo describe en 4.4.
tripleee
1
También puedes usar "let" así:
[~]$ var=1[~]$ let $var && echo "It's a number"|| echo "It's not a number"It\'s a number
[~]$ var=01[~]$ let $var && echo "It's a number"|| echo "It's not a number"It\'s a number
[~]$ var=toto
[~]$ let $var && echo "It's a number"|| echo "It's not a number"It\'s not a number
[~]$
Pero prefiero usar el operador "= ~" Bash 3+ como algunas respuestas en este hilo.
Voto negativo por falta de explicación. ¿Como funciona esto? Parece complejo y quebradizo, y no es obvio qué entradas aceptará exactamente. (Por ejemplo, es la eliminación de espacios crucialmente necesaria por qué va a decir un número con espacios incrustados es un número válido, que puede no ser deseable??.)
tripleee
1
Hizo lo mismo aquí con una expresión regular que prueba la parte completa y la parte de decimales, separadas con un punto.
¿Podría explicar por qué su respuesta es fundamentalmente diferente de otras respuestas antiguas, por ejemplo, la respuesta de Charles Duffy? Bueno, su respuesta en realidad está rota ya que valida un solo período.
gniourf_gniourf
no estoy seguro de entender el período único aquí ... es uno o cero períodos esperados ... Pero no hay nada fundamentalmente diferente, solo encontré la expresión regular más fácil de leer.
Jerome
también usando * debería coincidir con más casos del mundo real
Jerome
La cosa es que estás haciendo coincidir la cadena vacía a=''y la cadena que contiene un punto solo a='.'para que tu código esté un poco roto ...
gniourf_gniourf
0
Yo uso lo siguiente (para enteros):
## ##### constants#### __TRUE - true (0)## __FALSE - false (1)##
typeset -r __TRUE=0
typeset -r __FALSE=1## --------------------------------------## isNumber## check if a value is an integer ## usage: isNumber testValue ## returns: ${__TRUE} - testValue is a number else not##function isNumber {
typeset TESTVAR="$(echo "$1" | sed 's/[0-9]*//g' )"["${TESTVAR}"x =""x ]&&return ${__TRUE}||return ${__FALSE}}
isNumber $1
if[ $?-eq ${__TRUE}];then
print "is a number"fi
Casi correcto (estás aceptando la cadena vacía) pero gratificantemente complicado hasta el punto de ofuscación.
Gilles 'SO- deja de ser malvado'
2
Incorrecto: está aceptando -n, etc. (debido a echo), y está aceptando variables con nuevas líneas finales (debido a $(...)). Y, por cierto, printno es un comando de shell válido.
gniourf_gniourf
0
Probé la receta de ultrasawblade porque me pareció la más práctica y no pude hacer que funcionara. Al final, ideé otra forma, basada en otros en la sustitución de parámetros, esta vez con reemplazo de expresiones regulares:
[["${var//*([[:digit:]])}"]];&& echo "$var is not numeric"|| echo "$var is numeric"
Elimina cada carácter: dígito: clase en $ var y comprueba si nos queda una cadena vacía, lo que significa que el original eran solo números.
Lo que me gusta de este es su pequeño tamaño y flexibilidad. De esta forma, solo funciona para enteros de base 10 no delimitados, aunque seguramente puede usar la coincidencia de patrones para adaptarse a otras necesidades.
Leyendo la solución de mrucci, se ve casi igual que la mía, pero usando un reemplazo de cadena regular en lugar de "estilo sed". Ambos usan las mismas reglas para la coincidencia de patrones y son, AFAIK, soluciones intercambiables.
ata
sedes POSIX, mientras que su solución es bash. Ambos tienen sus usos
v010dya
0
Rápido y sucio: Sé que no es la forma más elegante, pero generalmente solo agregué un cero y probé el resultado. al igual que:
function isInteger {[ $(($1+0))!=0]&& echo "$1 is a number"|| echo "$1 is not a number"}
x=1; isInteger $x
x="1"; isInteger $x
x="joe"; isInteger $x
x=0x16; isInteger $x
x=-32674; isInteger $x
$ (($ 1 + 0)) devolverá 0 o bombardeará si $ 1 NO es un número entero. por ejemplo:
function zipIt {# quick zip - unless the 1st parameter is a number
ERROR="not a valid number. "if[ $(($1+0))!=0];then# isInteger($1)
echo " backing up files changed in the last $1 days."
OUT="zipIt-$1-day.tgz"
find .-mtime -$1 -type f -print0 | xargs -0 tar cvzf $OUT
return1fi
showError $ERROR
}
NOTA: Supongo que nunca pensé en buscar flotadores o tipos mixtos que hicieran que todo el guión de la bomba ... en mi caso, no quería que fuera más allá. Voy a jugar con la solución de mrucci y la expresión regular de Duffy: parecen las más robustas dentro del marco de bash ...
test && echo "foo" && exit 0 || echo "bar" && exit 1
enfoque que está utilizando puede tener algunos efectos secundarios no deseados: si el eco falla (tal vez la salida es a un FD cerrado),exit 0
se omitirá y el código lo intentaráecho "bar"
. Si falla en eso también, la&&
condición fallará, ¡y ni siquiera se ejecutaráexit 1
! El uso deif
declaraciones reales en lugar de&&
/||
es menos propenso a efectos secundarios inesperados.[[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }
los corchetes indican que las cosas NO deben ejecutarse en una subshell (que definitivamente sería así con()
paréntesis en su lugar). Advertencia: nunca te pierdas el punto y coma final . De lo contrario, podríabash
imprimir los mensajes de error más feos (y sin sentido) ...[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Respuestas:
Un enfoque es usar una expresión regular, así:
Si el valor no es necesariamente un número entero, considere enmendar la expresión regular adecuadamente; por ejemplo:
... o, para manejar números con un signo:
fuente
^-?
lugar de a^-*
menos que realmente estés haciendo el trabajo para manejar múltiples inversiones correctamente.[[ $yournumber =~ ^[0-9]+$ ]]
.Sin bashisms (funciona incluso en el Sistema V sh),
Esto rechaza cadenas vacías y cadenas que no contienen dígitos, aceptando todo lo demás.
Los números negativos o de coma flotante necesitan un trabajo adicional. Una idea es excluir
-
/.
en el primer patrón "malo" y agregar más patrones "malos" que contengan los usos inapropiados de ellos (?*-*
/*.*.*
)fuente
if test ...
${string#-}
(que no funciona en conchas Bourne antiguas, pero funciona en cualquier shell POSIX) para aceptar enteros negativos.'.' | *.*.*
a los patrones no permitidos y agregue puntos a los caracteres permitidos. Del mismo modo, puede permitir un signo opcional antes, aunque entonces preferiríacase ${string#[-+]}
simplemente ignorar el signo.La siguiente solución también se puede usar en shells básicos como Bourne sin la necesidad de expresiones regulares. Básicamente, cualquier operación de evaluación de valor numérico que utilice números que no sean números dará como resultado un error que se considerará implícitamente como falso en el shell:
como en:
También puedes probar por $? El código de retorno de la operación que es más explícito:
La redirección del error estándar está ahí para ocultar el mensaje de "expresión entera esperada" que bash imprime en caso de que no tengamos un número.
CUEVAS (gracias a los comentarios a continuación):
[[ ]]
lugar de[ ]
siempre evaluará atrue
true
bash: [[: 1 a: syntax error in expression (error token is "a")
bash: [[: i: expression recursion level exceeded (error token is "i")
fuente
[[ a -eq a ]]
se evalúa como verdadero (ambos argumentos se convierten a cero)if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
[
incorporado evaluará los argumentos como aritméticos. Eso es cierto tanto en ksh93 como en mksh. Además, dado que ambas matrices de soporte, hay una oportunidad fácil para la inyección de código. Use una coincidencia de patrón en su lugar.[[ ]]
pero no para[ ]
. Dicho esto, este comportamiento no está especificado por el estándar POSIX paratest
y en la propia documentación de bash; versiones futuras de bash podrían modificar el comportamiento para que coincida con ksh sin romper ninguna promesa de comportamiento documentada, por lo que no se garantiza que depender de su comportamiento actual persistente sea seguro.Esto prueba si un número es un número entero no negativo y es independiente de la shell (es decir, sin bashisms) y solo utiliza incorporaciones de shell:
INCORRECTO.
Como esta primera respuesta (a continuación) permite enteros con caracteres en ellos, siempre que los primeros no sean los primeros en la variable.
CORRECTO .
Como comentó y sugirió Jilles en su respuesta, esta es la forma correcta de hacerlo utilizando patrones de concha.
fuente
*[!0-9]*
es un patrón que coincide con todas las cadenas con al menos 1 carácter sin dígitos.${num##*[!0-9]*}
es una "expansión de parámetros" donde tomamos el contenido de lanum
variable y eliminamos la cadena más larga que coincide con el patrón. Si el resultado de la expansión del parámetro no está vacío (! [ -z ${...} ]
), entonces es un número, ya que no contiene ningún carácter que no sea un dígito.122s
:-(
Nadie sugirió la coincidencia de patrones extendida de bash :
fuente
shopt -s extglob
elimino de tu publicación (que he votado, es una de mis respuestas favoritas aquí), ya que en Construcciones condicionales puedes leer: Cuando se usan los operadores==
y!=
, la cadena a la derecha del operador se considera un patrón y coincide de acuerdo con las reglas descritas a continuación en Pattern Matching , como si laextglob
opción de shell estuviera habilitada. ¡Espero que no te moleste!shopt extglob
... ¡es bueno saberlo![[...]]
no están sujetas a división de palabras o expansión global.Estoy sorprendido por las soluciones que analizan directamente los formatos de números en shell. Shell no se adapta bien a esto, ya que es un DSL para controlar archivos y procesos. Hay numerosos analizadores numéricos un poco más abajo, por ejemplo:
Cambie '% f' al formato particular que requiera.
fuente
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
isnumber 23 && echo "this is a number" || echo "not a number"
2>/dev/null
, para queisnumber "foo"
no contamine stderr?isnumber "'a"
devolverá verdadero. Esto está documentado en la especificación POSIX donde leerá: Si el carácter inicial es una comilla simple o doble, el valor será el valor numérico en el conjunto de códigos subyacente del carácter que sigue a la comilla simple o doble .Estaba mirando las respuestas y ... ¡me di cuenta de que nadie pensaba en los números FLOAT (con punto)!
Usar grep también es genial.
-E significa expresión regular extendida
-q significa silencio (no hace eco)
-qE es la combinación de ambos.
Para probar directamente en la línea de comando:
Utilizando en un script bash:
Para hacer coincidir enteros JUST, use esto:
fuente
Solo un seguimiento a @mary. Pero debido a que no tengo suficiente representante, no pude publicar esto como un comentario a esa publicación. De todos modos, esto es lo que usé:
La función devolverá "1" si el argumento es un número, de lo contrario devolverá "0". Esto funciona tanto para enteros como para flotadores. El uso es algo como:
fuente
'BEGIN { exit(1-(a==a+0)) }'
es un poco difícil de asimilar pero se puede utilizar en una función que devuelve verdadero o falso como[
,grep -q
, etc.${i//[0-9]}
reemplaza cualquier dígito en el valor de$i
con una cadena vacía, verman -P 'less +/parameter\/' bash
.-z
comprueba si la cadena resultante tiene longitud cero.si también desea excluir el caso cuando
$i
está vacío, puede usar una de estas construcciones:fuente
man -P 'less +/parameter\/' bash
parte. Aprendiendo algo nuevo cada día. :)\-
expresiones regulares para abordar el problema. Úselo[0-9\-\.\+]
para contabilizar flotadores y números firmados.echo $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
Para mi problema, solo necesitaba asegurarme de que un usuario no ingresara accidentalmente algún texto, por lo que traté de mantenerlo simple y legible
De acuerdo con la página del manual, esto hace más o menos lo que quiero
Para evitar mensajes de error desagradables para cadenas que "podrían ser números", ignoro la salida de error
fuente
Antigua pregunta, pero solo quería agregar mi solución. Este no requiere ningún truco extraño de shell, ni depende de algo que no ha existido desde siempre.
Básicamente, solo elimina todos los dígitos de la entrada, y si te queda una cadena de longitud diferente de cero, entonces no era un número.
fuente
var
.$'0\n\n\n1\n\n\n2\n\n\n3\n'
.No puedo comentar aún, así que agregaré mi propia respuesta, que es una extensión de la respuesta de Glenn Jackman usando la coincidencia de patrones bash.
Mi necesidad original era identificar números y distinguir enteros y flotantes. Las definiciones de funciones deducidas a:
Utilicé pruebas unitarias (con shUnit2) para validar que mis patrones funcionaron según lo previsto:
Notas: El patrón isFloat se puede modificar para que sea más tolerante con el punto decimal (
@(.,)
) y el símbolo E (@(Ee)
). Las pruebas de mi unidad solo prueban valores que son enteros o flotantes, pero no una entrada no válida.fuente
Yo intentaría esto:
Nota: esto reconoce nan e inf como número.
fuente
%f
es probablemente mejor de todos modos)if
sí mismo? Eso es loif
que ...if printf "%g" "$var" &> /dev/null; then ...
'a
.@Charles Dufy y otros ya han dado una respuesta clara. Una solución bash pura usaría lo siguiente:
Aunque para los números reales no es obligatorio tener un número antes del punto radix .
Para proporcionar un soporte más completo de números flotantes y notación científica (muchos programas en C / Fortran o exportarán flotantes de esta manera), una adición útil a esta línea sería la siguiente:
Por lo tanto, conduce a una forma de diferenciar los tipos de números, si está buscando algún tipo específico:
Nota: Podríamos enumerar los requisitos sintácticos para la notación decimal y científica, uno de ellos para permitir la coma como punto de raíz, así como ".". Entonces afirmaríamos que debe haber solo uno de esos puntos radix. Puede haber dos signos +/- en un flotador [Ee]. Aprendí algunas reglas más del trabajo de Aulu, y probé contra cadenas malas como '' '-' '-E-1' '0-0'. Aquí están mis herramientas regex / substring / expr que parecen estar resistiendo:
fuente
¡No olvides
-
incluir números negativos!fuente
=~
existió al menos desde Bash 3.0.Esto se puede lograr usando
grep
para ver si la variable en cuestión coincide con una expresión regular extendida.Prueba de entero
1120
:Salida:
Valid number.
Prueba no entera
1120a
:Salida:
Error: not a number.
Explicación
grep
, el-E
interruptor nos permite usar expresiones regulares extendidas'^[0-9]+$'
. Esta expresión regular significa que la variable solo debe[]
contener los números del0-9
cero al nueve desde el^
principio hasta el$
final de la variable y debe tener al menos+
un carácter.grep
, el-q
interruptor de silencio se apaga cualquier salida si es o no encuentra nada.if
comprueba el estado de salida degrep
. El estado de salida0
significa éxito y cualquier cosa mayor significa un error. Elgrep
comando tiene un estado de salida de0
si encuentra una coincidencia y1
cuándo no;Entonces, al ponerlo todo junto, en la
if
prueba, hacemosecho
la variable$yournumber
y la|
canalizamos a lagrep
que con el-q
interruptor coincide silenciosamente con la-E
expresión de'^[0-9]+$'
expresión regular extendida . El estado de salida degrep
será0
sigrep
se encuentra con éxito una coincidencia y1
si no. Si lo conseguimos, nosotrosecho "Valid number."
. Si no coincide, nosotrosecho "Error: not a number."
.Para carrozas o dobles
Simplemente podemos cambiar la expresión regular de
'^[0-9]+$'
a'^[0-9]*\.?[0-9]+$'
para flotantes o dobles.Prueba de flotación
1120.01
:Salida:
Valid number.
Prueba de flotación
11.20.01
:Salida:
Error: not a number.
Para negativos
Para permitir enteros negativos, simplemente cambie la expresión regular de
'^[0-9]+$'
a'^\-?[0-9]+$'
.Para permitir flotantes o dobles negativos, simplemente cambie la expresión regular de
'^[0-9]*\.?[0-9]+$'
a'^\-?[0-9]*\.?[0-9]+$'
.fuente
[-]
lugar de\-
y en[.]
lugar de\.
es un poco más detallado, pero significa que sus cadenas no tienen que cambiar si ' Reutilizado en un contexto donde las barras invertidas se consumen).http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
También puedes usar las clases de personajes de bash.
Los números incluirán espacio, el punto decimal y "e" o "E" para coma flotante.
Pero, si especifica un número hexadecimal de estilo C, es decir, "0xffff" o "0XFFFF", [[: dígito:]] devuelve verdadero. Un poco de trampa aquí, bash te permite hacer algo como "0xAZ00" y aún contarlo como un dígito (¿no es esto de alguna peculiaridad extraña de los compiladores de GCC que te permiten usar la notación 0x para bases distintas de 16 ??? )
Es posible que desee probar "0x" o "0X" antes de probar si es un valor numérico si su entrada no es de confianza, a menos que desee aceptar números hexadecimales. Eso se lograría mediante:
fuente
[[ $VAR = *[[:digit:]]* ]]
devolverá verdadero si la variable contiene un número, no si es un número entero.[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"
impresionesnumeric
. Probado en la versión bash3.2.25(1)-release
.[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
La forma más simple es verificar si contiene caracteres que no sean dígitos. Reemplaza todos los caracteres de dígitos con nada y verifica la longitud. Si hay longitud, no es un número.
fuente
Como tuve que manipular esto últimamente y me gusta enfoque de karttu con la prueba de la unidad más. Revisé el código y agregué algunas otras soluciones también, pruébelo usted mismo para ver los resultados:
Entonces isNumber () incluye guiones, comas y notación exponencial y, por lo tanto, devuelve TRUE en enteros y flotantes, mientras que isFloat () devuelve FALSE en valores enteros y isInteger () también devuelve FALSE en flotantes. Para su conveniencia, todo en uno:
fuente
function
palabra clave ya que no hace nada útil. Además, no estoy seguro de la utilidad de los valores de retorno. A menos que se especifique lo contrario, las funciones devolverán el estado de salida del último comando, por lo que no necesita hacerreturn
nada usted mismo.return
s son confusos y lo hacen menos legible. Usarfunction
palabras clave o no es más una cuestión de sabor personal, al menos las eliminé de los revestimientos para ahorrar espacio. gracias.Yo uso expr . Devuelve un valor distinto de cero si intenta agregar un cero a un valor no numérico:
Podría ser posible usar bc si necesita números no enteros, pero no creo que
bc
tenga el mismo comportamiento. Agregar cero a un no número te da cero y también devuelve un valor de cero. Quizás puedas combinarbc
yexpr
. Usebc
para agregar cero a$number
. Si la respuesta es0
, intenteexpr
verificar que$number
no sea cero.fuente
expr -- "$number" + 0
; sin embargo, esto seguirá pretendiendo eso0 isn't a number
. Deman expr
:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
Me gusta la respuesta de Alberto Zaccagni.
Requisitos previos importantes: - no se generan subcapas - no se invocan analizadores RE - la mayoría de las aplicaciones de shell no usan números reales
Pero si
$var
es complejo (por ejemplo, un acceso de matriz asociativo), y si el número será un número entero no negativo (la mayoría de los casos de uso), ¿entonces esto es quizás más eficiente?fuente
Utilizo printf como se menciona en otras respuestas, si proporciona la cadena de formato "% f" o "% i" printf hará la comprobación por usted. Más fácil que reinventar los cheques, la sintaxis es simple y corta y printf es omnipresente. Por lo tanto, es una opción decente en mi opinión: también puede usar la siguiente idea para verificar una variedad de cosas, no solo es útil para verificar números.
>$ var=45
>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }
fuente
golpetazo expresión regular vsgolpetazo expansión de parámetros
Como la respuesta de Charles Duffy funciona, pero solo usagolpetazoexpresión regular , y sé que esto es lento, me gustaría mostrar otra forma, usando sologolpetazoexpansión de parámetros :
Solo para enteros positivos:
Para enteros:
Luego para flotar:
Para que coincida con la solicitud inicial:
Ahora una pequeña comparación:
Hay un mismo control basado en la respuesta de Charles Duffy :
Algunas pruebas:
OK eso está bien. Ahora, ¿cuánto tiempo llevará todo esto (en mi frambuesa pi):
Luego con expresiones regulares:
fuente
extglob
(y un poco más rápido)!Para atrapar números negativos:
fuente
When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled.
gnu.org/software/bash/manual/bashref.html#index-_005b_005bTambién puedes usar "let" así:
Pero prefiero usar el operador "= ~" Bash 3+ como algunas respuestas en este hilo.
fuente
Elimine el
-\?
patrón de coincidencia in grep si no acepta un entero negativo.fuente
Hizo lo mismo aquí con una expresión regular que prueba la parte completa y la parte de decimales, separadas con un punto.
fuente
.
a=''
y la cadena que contiene un punto soloa='.'
para que tu código esté un poco roto ...Yo uso lo siguiente (para enteros):
fuente
-n
, etc. (debido aecho
), y está aceptando variables con nuevas líneas finales (debido a$(...)
). Y, por cierto,print
no es un comando de shell válido.Probé la receta de ultrasawblade porque me pareció la más práctica y no pude hacer que funcionara. Al final, ideé otra forma, basada en otros en la sustitución de parámetros, esta vez con reemplazo de expresiones regulares:
Elimina cada carácter: dígito: clase en $ var y comprueba si nos queda una cadena vacía, lo que significa que el original eran solo números.
Lo que me gusta de este es su pequeño tamaño y flexibilidad. De esta forma, solo funciona para enteros de base 10 no delimitados, aunque seguramente puede usar la coincidencia de patrones para adaptarse a otras necesidades.
fuente
sed
es POSIX, mientras que su solución esbash
. Ambos tienen sus usosRápido y sucio: Sé que no es la forma más elegante, pero generalmente solo agregué un cero y probé el resultado. al igual que:
$ (($ 1 + 0)) devolverá 0 o bombardeará si $ 1 NO es un número entero. por ejemplo:
NOTA: Supongo que nunca pensé en buscar flotadores o tipos mixtos que hicieran que todo el guión de la bomba ... en mi caso, no quería que fuera más allá. Voy a jugar con la solución de mrucci y la expresión regular de Duffy: parecen las más robustas dentro del marco de bash ...
fuente
1+1
, pero rechaza algunos enteros positivos con0
s iniciales (porque08
es una constante octal no válida).0
no es un número, y que está sujeta a la inyección de código arbitrario, probarlo:isInteger 'a[$(ls)]'
. Ooops$((...))
no se cita, un númeroIFS=123
lo cambiará.