Estoy tratando de hacer algo lo suficientemente común: analizar la entrada del usuario en un script de shell. Si el usuario proporcionó un número entero válido, el script hace una cosa, y si no es válido, hace otra. El problema es que no he encontrado una manera fácil (y razonablemente elegante) de hacer esto; no quiero tener que separarlo char por char.
Sé que esto debe ser fácil, pero no sé cómo. Podría hacerlo en una docena de idiomas, ¡pero no en BASH!
En mi investigación encontré esto:
Expresión regular para probar si una cadena consta de un número real válido en base 10
Y hay una respuesta allí que habla de expresiones regulares, pero que yo sepa, esa es una función disponible en C (entre otras). Aún así, tenía lo que parecía una gran respuesta, así que lo probé con grep, pero grep no sabía qué hacer con él. Probé -P que en mi caja significa tratarlo como una expresión regular PERL - nada. Dash E (-E) tampoco funcionó. Y tampoco -F.
Para que quede claro, estoy intentando algo como esto, buscando cualquier resultado; a partir de ahí, piratearé el script para aprovechar lo que obtenga. (IOW, esperaba que una entrada no conforme no devolviera nada mientras se repite una línea válida).
snafu=$(echo "$2" | grep -E "/^[-+]?(?:\.[0-9]+|(?:0|[1-9][0-9]*)(?:\.[0-9]*)?)$/")
if [ -z "$snafu" ] ;
then
echo "Not an integer - nothing back from the grep"
else
echo "Integer."
fi
¿Alguien podría ilustrar cómo se hace esto más fácilmente?
Francamente, esto es un defecto de TEST, en mi opinión. Debería tener una bandera como esta
if [ -I "string" ] ;
then
echo "String is a valid integer."
else
echo "String is not a valid integer."
fi
fuente
[
es viejo compatibletest
;[[
es lo nuevo de Bash, con más operaciones y diferentes reglas de cotización. Si ya ha decidido seguir con Bash, hágalo[[
(es mucho mejor); si necesita portabilidad a otras conchas, evítelo por[[
completo.Respuestas:
^
indica el principio del patrón de entrada-
es un literal "-"?
medios "0 o 1 del (-
) anterior "+
medios "1 o más de los ([0-9]
) anteriores "$
indica el final del patrón de entradaEntonces, la expresión regular coincide con un opcional
-
(para el caso de números negativos), seguido de uno o más dígitos decimales.Referencias :
fuente
+
significa "1 o más de los anteriores" y el$
indica el final del patrón de entrada. Entonces, la expresión regular coincide con un opcional-
seguido de uno o más dígitos decimales.[A-z]
no sólo le daráA-Z
ya-z
sino también\
,[
,]
,^
,_
, y`
.d[g-i]{2}
podría terminar no solo coincidente,dig
sino tambiéndish
en la intercalación sugerida por esa respuesta (donde elsh
dígrafo se considera un solo carácter, intercalado despuésh
).Vaya ... ¡¡Aquí hay tantas buenas soluciones !! De todas las soluciones anteriores, estoy de acuerdo con @nortally en que usar el
-eq
delineador es lo mejor.Estoy ejecutando GNU bash, versión
4.1.5
(Debian). También he comprobado esto en ksh (SunSO 5.10).Aquí está mi versión de verificar si
$1
es un número entero o no:Este enfoque también tiene en cuenta los números negativos, que algunas de las otras soluciones tendrán un resultado negativo defectuoso, y permitirá un prefijo de "+" (por ejemplo, +30) que obviamente es un número entero.
Resultados:
La solución proporcionada por Ignacio Vazquez-Abrams también fue muy ordenada (si te gusta la expresión regular) después de que se explicó. Sin embargo, no maneja números positivos con el
+
prefijo, pero se puede arreglar fácilmente de la siguiente manera:fuente
Llegado tarde a la fiesta aquí. Estoy extremadamente sorprendido de que ninguna de las respuestas mencione la solución más simple, rápida y portátil; la
case
declaración.El recorte de cualquier signo antes de la comparación parece un truco, pero eso hace que la expresión de la declaración de caso sea mucho más simple.
fuente
''|*[!0-9]*)
Me gusta la solución que usa la
-eq
prueba, porque es básicamente una sola línea.Mi propia solución fue usar la expansión de parámetros para tirar todos los números y ver si quedaba algo. (Todavía estoy usando 3.0, no lo he usado
[[
oexpr
antes, pero me alegra conocerlos).fuente
[ -z "${INPUT_STRING//[0-9]}" ]
una solución realmente agradable!-eq
solución tiene algunos problemas; ver aquí: stackoverflow.com/a/808740/1858225Para la portabilidad a pre-Bash 3.1 (cuando
=~
se introdujo la prueba), useexpr
.expr STRING : REGEX
busca REGEX anclado al comienzo de STRING, repitiendo el primer grupo (o la duración de la coincidencia, si no hay ninguno) y devolviendo éxito / fracaso. Esta es la antigua sintaxis de expresiones regulares, de ahí el exceso\
.-\?
significa "tal vez-
",[0-9]\+
significa "uno o más dígitos" y$
significa "fin de cadena".Bash también admite globs extendidos, aunque no recuerdo de qué versión en adelante.
@(-|)
significa "-
o nada",[0-9]
significa "dígito" y*([0-9])
significa "cero o más dígitos".fuente
awk
,~
era el operador "regex match". En Perl (como se copió de C),~
ya se usaba para "complemento de bits", por lo que usaron=~
. Esta notación posterior se copió a varios otros idiomas. (Perl 5.10 y Perl 6 prefieren~~
más, pero eso no tiene ningún impacto aquí). Supongo que podrías verlo como una especie de igualdad aproximada ...Aquí hay otra versión (solo usando el comando integrado de prueba y su código de retorno):
fuente
$()
conif
. Esto funciona:if is_int "$input"
. Además, el$[]
formulario está obsoleto. Úselo en su$(())
lugar. En el interior de ambos, el signo de dólar se puede omitir: lasecho "Integer: $((input))"
llaves no son necesarias en ninguna parte de su guión.test
no parece apoyar esto.[[
aunque lo hace.[[ 16#aa -eq 16#aa ]] && echo integer
imprime "entero".[[
devuelve falsos positivos para este método; por ejemplo,[[ f -eq f ]]
tiene éxito. Entonces debe usartest
o[
.Puede quitar los que no son dígitos y hacer una comparación. Aquí hay un script de demostración:
Así es como se ve la salida de prueba:
fuente
${var//string}
y${var#string}
y en la sección llamada "Coincidencia de patrones" para [^ [: dígito:]] `(que también se trata enman 7 regex
).match=${match#0*}
no no despojar ceros a la izquierda, se despoja a lo más un cero. Usando la expansión, esto solo se puede lograr usando aextglob
través dematch=${match##+(0)}
.09
no es un número entero si considera que un número entero no tiene ceros a la izquierda. La prueba es si input (09
) es igual a una versión desinfectada (9
- un número entero) y no es así.Para mí, la solución más simple fue usar la variable dentro de una
(())
expresión, así:Por supuesto, esta solución solo es válida si un valor de cero no tiene sentido para su aplicación. Eso pasó a ser cierto en mi caso, y esto es mucho más simple que las otras soluciones.
Como se señaló en los comentarios, esto puede hacer que esté sujeto a un ataque de ejecución de código: el
(( ))
operador evalúaVAR
, como se indica en laArithmetic Evaluation
sección de la página de manual de bash (1) . Por lo tanto, no debe usar esta técnica cuando la fuente del contenido deVAR
es incierta (ni debe usar CUALQUIER otra forma de expansión variable, por supuesto).fuente
if (( var )); then echo "$var is an int."; fi
VAR='a[$(ls)]'; if ((VAR > 0)); then echo "$VAR is a positive integer"; fi
. En este punto que eres me alegro no introdujo alguna orden mal en vez dels
. Debido a que OP menciona la entrada del usuario , ¡realmente espero que no esté usando esto con la entrada del usuario en el código de producción!agent007
o con sed:
fuente
test -z "${string//[0-9]/}" && echo "integer" || echo "no integer"
... aunque eso básicamente duplica la respuesta de Dennis Williamsonif [[ -n "$(printf "%s" "${2}" | sed s/[0-9]//g)" ]]; then
Agregando a la respuesta de Ignacio Vazquez-Abrams. Esto permitirá que el signo + preceda al número entero, y permitirá cualquier número de ceros como puntos decimales. Por ejemplo, esto permitirá que +45.00000000 se considere un número entero.
Sin embargo, $ 1 debe formatearse para contener un punto decimal. 45 no se considera un número entero aquí, pero 45.0 sí lo es.
fuente
^[-+]?[0-9]
...?Para las risas, desarrollé rápidamente un conjunto de funciones para hacer esto (is_string, is_int, is_float, es una cadena alfa u otra) pero hay formas más eficientes (menos código) de hacer esto:
Ejecute algunas pruebas aquí, definí que -44 es un int pero 44- no lo es, etc.:
Salida:
NOTA: Los ceros iniciales podrían inferir algo más al agregar números como octal, por lo que sería mejor eliminarlos si tiene la intención de tratar '09' como un int (lo que estoy haciendo) (por ejemplo,
expr 09 + 0
o eliminar con sed)fuente