El libro que estoy leyendo: Learning the Bash Shell de O'Reilly especifica algún código de la siguiente manera:
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
howmany=$1
shift
....
....
etc
Esto utiliza la
grep
utilidad de búsqueda para probar si$1
coincide con el patrón apropiado. Para hacer esto, proporcionamos la expresión regular^-[0-9][0-9]*$
grep, que se interpreta como "un guión inicial seguido de un dígito, seguido opcionalmente de uno o más dígitos". Si se encuentra una coincidencia,grep
se devolverá la coincidencia y la prueba será verdadera; de lo contrario,grep
no se devolverá nada y el procesamiento pasará a laelif
prueba.Observe que hemos incluido la expresión regular entre comillas simples para evitar que el shell interprete $ y *, y los pasamos a grep sin modificar.
Entonces, ¿por qué la expresión regular no '^-[0-9]'
pierde su significado como entre comillas simples? Normalmente, todo lo que está entre comillas simples pierde su significado.
Gracias por su ayuda.
Respuesta1
Mientras otros han respondido a su pregunta específica, agregaré eso
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
Es una forma incorrecta de comprobar si una cadena coincide con una expresión regular por varios motivos:
- No se puede utilizar
echo
para datos arbitrarios. - Dejar una expansión de parámetro sin comillas como la
$1
anterior es el operador split+glob. grep
no compara la expresión regular con su entrada completa, sino con cada línea de su entrada. Entonces volvería verdadero,foo\n-0\nbar
por ejemplo.- Una expresión regular puede coincidir con una longitud cero, por lo que en el caso general es incorrecto verificar si
grep
produce algún resultado (tenga en cuenta que la sustitución de comandos elimina los caracteres de nueva línea finales). Es mejor utilizarlogrep -q
y confiar en el estado de salida degrep
, en lugar de en el de[
, y también evitar la sustitución de comandos. - Tenga en cuenta que ese
grep
comando se puede simplificar agrep -xE '-[0-9]+'
bash
y ksh93
tener zsh
un operador dedicado para la coincidencia de expresiones regulares (extendidas). Para usarlo de forma portátil y confiable en los tres (y en bash-3.1), la sintaxis es:
re='^-[0-9]+$'
if [[ $1 =~ $re ]]; then
echo matches
fi
yash
y zsh
también soporte:
if [ "$1" '=~' '^-[0-9]+$' ]; then
echo matches
fi
El comando estándar para hacer coincidencias de expresiones regulares de cadenas (básicas) es expr
:
if expr " $1" : ' -[0-9]\{1,\}$' > /dev/null; then
echo matches
fi
Tenga en cuenta que ^
(pero no $
) está implícito en expr
. También usamos ese carácter de espacio inicial para evitar problemas con valores de $1
que resultan ser expr
operadores.
También tenga en cuenta que si la expresión regular contiene \(...\)
, afectará el comportamiento de expr
.
Considerándolo todo, es mejor usar awk
otra forma estándar/portátil de hacerlo (tenga en cuenta que awk
usa expresiones regulares extendidas):
if STRING=$1 RE='^-[0-9]+$' awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'; then
...
O usar una función:
re_match() {
STRING=$1 RE=$2 awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'
}
if re_match "$1" '^-[0-9]+$'
En este caso, también puedes lograrlo con una case
construcción estándar:
case $1 in
("" | *[!0-9-]* | [!-]* | - | ?*-*) ;;
(*) echo match;;
esac
Para usarlo grep
, puede usarlo con la --null
opción (cuando sea compatible, ya que no es una opción estándar) para indicarle que funcione en registros delimitados NUL en lugar de registros delimitados por nueva línea. Dado que en la mayoría de los shells $1
no puede contener NUL, eso debería ser seguro:
if printf %s "$1" | grep --null -xq '-[0-9]+$'; then
echo match
fi
Respuesta2
Las comillas simples le dicen al shell que mantenga los caracteres adjuntos como están,sin ninguna interpretación. La cadena entre comillas se pasa tal cual a grep
, sin las comillas: cuando grep
busca sus argumentos, ve
grep
y
^-[0-9][0-9]*$
y actúa en consecuencia. (LeerCómo se ejecutan los programassi tiene curiosidad acerca de la construcción de argumentos en Linux).
bash
y grep
son diferentes. La forma en que este comando usa comillas garantiza que bash
no procese la cadena, pero grep
lo hace.
Respuesta3
Las comillas simples previenenglobo(permitiendo bash
interpretar comodines como *
) y expansión de variables mediante el uso de $
. Básicamente, estás diciendo bash
"tomar literalmente esos caracteres y pasárselos a grep
". Cuando grep
los ve, está diseñado para comprender las expresiones regulares, por lo queentoncesla expresión regular se interpreta dentro grep
.
Versión más corta: los argumentos entre comillas simples proporcionan un medio para escapar del procesamiento desde su shell antes de que el argumento se pase al comando.
Respuesta4
Pierde su significado. grep
utiliza casi los mismos patrones de expresiones regulares que bash.