una - variables de entorno linux bash
¿Cómo puedo probar si una variable es un número en Bash? (30)
Simplemente no puedo entender cómo me aseguro de que un argumento pasado a mi script sea un número o no.
Todo lo que quiero hacer es algo como esto:
test *isnumber* $1 && VAR=$1 || echo "need a number"
¿Alguna ayuda?
Antigua pregunta, pero solo quería seguir con mi solución. Este no requiere ningún truco de shell extraño, o confiar en 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 quedan con una cadena que no es de cero, entonces no fue un número.
Como tuve que manipular esto últimamente y como karttu nombrar con la prueba de la unidad más. Revisé el código y también agregué algunas otras soluciones, pruébelo usted mismo para ver los resultados:
#!/bin/bash
# N={0,1,2,3,...} by syntaxerror
function isNaturalNumber()
{
[[ ${1} =~ ^[0-9]+$ ]]
}
# Z={...,-2,-1,0,1,2,...} by karttu
function isInteger()
{
[[ ${1} == ?(-)+([0-9]) ]]
}
# Q={...,-½,-¼,0.0,¼,½,...} by karttu
function isFloat()
{
[[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
# R={...,-1,-½,-¼,0.E+n,¼,½,1,...}
function isNumber()
{
isNaturalNumber $1 || isInteger $1 || isFloat $1
}
bools=("TRUE" "FALSE")
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"
false_values="blah meh mooh blah5 67mooh a123bc"
for value in ${int_values} ${float_values} ${false_values}
do
printf " %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value)
printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value)
printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value)
printf "%5s=%-24s/n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value)
done
Así que isNumber () incluye guiones, comas y notación exponencial y, por lo tanto, devuelve VERDADERO en enteros y flotantes, mientras que, por otro lado, isFloat () devuelve FALSE en valores enteros y isInteger () también devuelve FALSE en floats. Para su conveniencia todos como liners:
isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]]; }
isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; }
isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; }
Estaba mirando las respuestas y ... ¡me di cuenta de que nadie pensó en los números de 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:
$ echo "32" | grep -E ^/-?[0-9]?/.?[0-9]+$
# answer is: 32
$ echo "3a2" | grep -E ^/-?[0-9]?/.?[0-9]+$
# answer is empty (false)
$ echo ".5" | grep -E ^/-?[0-9]?/.?[0-9]+$
# answer .5
$ echo "3.2" | grep -E ^/-?[0-9]?/.?[0-9]+$
# answer is 3.2
Usando en un script bash:
check=`echo "$1" | grep -E ^/-?[0-9]*/.?[0-9]+$`
if [ "$check" != '''' ]; then
# it IS numeric
echo "Yeap!"
else
# it is NOT numeric.
echo "nooop"
fi
Para hacer coincidir enteros JUSTOS, usa esto:
# change check line to:
check=`echo "$1" | grep -E ^/-?[0-9]+$`
Esto prueba si un número no es un número entero negativo y es independiente de shell (es decir, sin bashismos) y usa solo shell incorporado:
[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";
PERO ES INCORRECTO .
Como jilles comentó y sugirió en jilles esta es la forma correcta de hacerlo usando patrones de shell.
[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";
He encontrado una versión bastante corta:
function isnum()
{
return `echo "$1" | awk -F"/n" ''{print ($0 != $0+0)}''`
}
La forma más sencilla es verificar si contiene caracteres sin 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
La siguiente solución también se puede utilizar en shells básicos como Bourne sin necesidad de expresiones regulares. Básicamente, cualquier operación de evaluación de valor numérico que use números que no sean números resultará en 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 puede 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 imprime bash 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
[[ ]]
lugar de[ ]
siempre se evaluará comotrue
- 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")
Me sorprende que las soluciones analicen directamente los formatos de números en shell. Shell no es adecuado para esto, ya que es un DSL para controlar archivos y procesos. Hay muchos analizadores de números 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
}
Cambie ''% f'' a cualquier formato particular que requiera.
Nadie sugirió la coincidencia de patrones extendida de bash:
[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"
No puedo comentar aún, así que agregaré mi propia respuesta, que es una extensión de la respuesta de glenn jackman utilizando la coincidencia de patrones de bash.
Mi necesidad original era identificar números y distinguir enteros y flotadores. 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 de unidad (con shUnit2) para validar mis patrones que 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}"
done
for 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)
). Mis pruebas de unidad solo prueban valores que son enteros o flotantes, pero no entradas inválidas.
Para atrapar números negativos:
if [[ $1 == ?(-)+([0-9.]) ]]
then
echo number
else
echo not a number
fi
Probé la receta de ultrasawblade ya que me pareció la más práctica, y no pude hacerla funcionar. Sin embargo, al final diseñé otra forma, basada como otras en la sustitución de parámetros, esta vez con el reemplazo de expresiones regulares:
[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"
Elimina cada carácter de: dígito: clase en $ var y comprueba si nos queda una cadena vacía, lo que significa que el original era solo números.
Lo que me gusta de este es su pequeña huella y flexibilidad. De esta forma, solo funciona con enteros de base 10 no delimitados, aunque seguramente puede usar la coincidencia de patrones para adaptarla a otras necesidades.
Rápido y sucio: Sé que no es la forma más elegante, pero por lo general le 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 bomba 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
return 1
fi
showError $ERROR
}
NOTA: Supongo que nunca pensé en buscar flotadores o tipos mixtos que harán que todo el script sea una bomba ... en mi caso, no quería que fuera más allá. Voy a jugar un poco con la solución de mrucci y la expresión regular de Duffy, parecen ser las más robustas dentro del marco de bash ...
Sólo un seguimiento a @mary. Pero como no tengo suficiente representante, no pude publicar esto como un comentario para esa publicación. De todos modos, aquí está lo que he usado:
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
Sin bashismos (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 tienen dígitos, aceptando todo lo demás.
Los números negativos o de punto flotante necesitan algún trabajo adicional. Una idea es excluir -
.
en el primer patrón "malo" y agregue más patrones "malos" que contengan los usos inapropiados de ellos ( ?*-*
/ *.*.*
)
Un enfoque es usar una expresión regular, como así:
re=''^[0-9]+$''
if ! [[ $yournumber =~ $re ]] ; then
echo "error: Not a number" >&2; exit 1
fi
Si el valor no es necesariamente un número entero, considere enmendar la expresión regular de manera apropiada; por ejemplo:
^[0-9]+([.][0-9]+)?$
... o, para manejar números con un signo:
^[+-]?[0-9]+([.][0-9]+)?$
Ya se ha dado una respuesta clara por @charles Dufy y otros. Una solución de bash pura estaría usando 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 los números flotantes y la notación científica (muchos programas en C / Fortran o bien exportarán 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, puede buscar 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 los cuales es permitir la coma como punto de raíz, así como ".". Entonces, afirmaríamos que solo debe haber uno de estos 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 aguantar:
parse_num() {
local r=`expr "$1" : ''.*/([.,]/)'' 2>/dev/null | tr -d ''/n''`
nat=''^[+-]?[0-9]+[.,]?$'' /
dot="${1%[.,]*}${r}${1##*[.,]}" /
float=''^[/+/-]?([.,0-9]+[Ee]?[-+]?|)[0-9]+$''
[[ "$1" == $dot ]] && [[ "$1" =~ $float ]] || [[ "$1" =~ $nat ]]
} # usage: parse_num -123.456
Yo intentaría esto:
printf "%g" "$var" &> /dev/null
if [[ $? == 0 ]] ; then
echo "$var is a number."
else
echo "$var is not a number."
fi
Nota: esto reconoce a nan e inf como número.
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>&1
then
echo "$number is a number"
else
echo "$number isn''t a number"
fi
Podría ser posible usar bc si necesita no enteros, pero no creo que bc
tenga el mismo comportamiento. Agregar cero a un no-número le da cero y también devuelve un valor de cero. Tal vez puedas combinar bc
y expr
. Use bc
para agregar cero a $number
. Si la respuesta es 0
, intente expr
para verificar que $number
no es cero.
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
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
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 el espacio, el punto decimal y "e" o "E" para el punto flotante.
Pero, si especifica un número hexadecimal de estilo C, es decir, "0xffff" o "0XFFFF", [[: dígito:]] devuelve verdadero. Una pequeña trampa aquí, bash te permite hacer algo como "0xAZ00" y aún así contarla como un dígito (¿no es esto de alguna extraña peculiaridad de los compiladores GCC que te permiten usar la notación 0x para otras bases que no sean 16? )
Es posible que desee probar "0x" o "0X" antes de probar si es numérico si su entrada no es confiable, 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
Hizo lo mismo aquí con una expresión regular que prueba la parte entera y la parte de los decimales, separados con un punto.
re="^[0-9]*[.]{0,1}[0-9]*$"
if [[ $1 =~ $re ]]
then
echo "is numeric"
else
echo "Naahh, not numeric"
fi
Me gusta la respuesta de Alberto Zaccagni.
if [ "$var" -eq "$var" ] 2>/dev/null; then
Prerrequisitos importantes: - no se generan subshells - 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 asociativa), y si el número será un entero no negativo (en la mayoría de los casos de uso), ¿quizás esto sea más eficiente?
if [ "$var" -ge 0 ] 2> /dev/null; then ..
Podrías usar "let" también 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.
Siguiendo con share de share 13 de octubre, si usar expr
esto podría ser mejor
test_var=`expr $am_i_numeric /* 0` >/dev/null 2>&1
if [ "$test_var" = "" ]
then
......
Si es numérico, multiplicado por 1 le da el mismo valor (incluidos los números negativos). De lo contrario obtienes null
lo que puedes probar
Uso printf como otras respuestas mencionadas, 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 la impresión es ubicua. Así que, en mi opinión, es una opción decente: también puede utilizar la siguiente idea para verificar una serie 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=1
local -i FAIL=0
case "${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; }
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"
¡No olvides incluir números negativos!
printf ''%b'' "-123/nABC" | tr ''[:space:]'' ''_'' | grep -q ''^-/?[[:digit:]]/+$'' && echo "Integer." || echo "NOT integer."
Elimine el -/?
patrón de coincidencia en grep si no acepta un entero negativo.
test -z "${i//[0-9]}" && echo digits || echo no no no
${i//[0-9]}
reemplaza cualquier dígito en el valor de $i
con una cadena vacía, vea man -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:
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
variable a verificar
number=12345
onumber=-23234
onumber=23.167
onumber=-345.234
comprobar numérico o no numérico
echo $number | grep -E ''^-?[0-9]*/.?[0-9]*$'' > /dev/null
Decidir sobre nuevas acciones basadas en el estado de salida de los anteriores
if [ $? -eq 0 ]; then echo "Numeric"; else echo "Non-Numeric"; fi