Quiero usarlo grep
para verificar si un comando llamado cmd
que se ejecutó contiene la palabra name
como argumento. (Lo sé !*
en Bash, no quiero usarlo).
Digamos que tengo el comando en una variable llamada process
.
Al principio lo intenté echo $process | grep 'cmd name'
. Esto no es lo suficientemente refinado, ya que sólo tiene en cuenta un argumento.
Entonces traté de ver si podía capturarlo como último argumento usando la expresión regular cmd .*(?= )name
. La idea es capturar cualquier cosa, seguido de un espacio, seguido de name
(después cmd
). Pero esto no funciona.
Mi objetivo, si esto funcionaba, era tratar de explicar que fuera un argumento en cualquiera de las posiciones.
¿Cómo se pueden utilizar expresiones regulares para hacer esto?
Respuesta1
En la respuesta a continuación, evito explícitamente el uso de grep
. Una línea de comando es una lista de elementos separados y debe analizarse como tal, no como una línea de texto.
Suponiendo que mantiene tanto el comando como los argumentos del comando en una sola cadena (sería mejor usar una matriz, consulte¿Cómo podemos ejecutar un comando almacenado en una variable?), entonces puedes hacer algo como
process='cmd with "some arguments" and maybe name'
set -f
set -- $process
if [ "$1" != 'cmd' ]; then
echo 'The command is not "cmd"'
exit 1
fi
shift
for arg do
if [ "$arg" = 'name' ]; then
echo 'Found "name" argument'
exit
fi
done
echo 'Did not find "name" argument in command line'
exit 1
Esto primero deshabilitaría la generación de nombres de archivos, porque queremos usar $process
sin comillas para dividirlo en palabras separadas, y si esa cadena contiene patrones globales de nombres de archivos (como *
), arruinaría nuestro análisis. Esto lo hacemos con set -f
.
Luego configuramos los parámetros posicionales para las palabras en $process
. Después de eso, si "$1"
es cmd
, sabemos que debemos buscarlo name
en el resto de la línea de comando. Si no, nos detenemos ahí.
Salimos de la lista de parámetros posicionales y comenzamos shift
a cmd
mirar los argumentos en un bucle. En cada iteración, simplemente comparamos el argumento con la cadena name
. Si lo encontramos lo decimos y salimos.
Al final del ciclo, sabemos que no hemos encontrado el name
argumento, por lo que informamos esto y salimos con un error.
Tenga en cuenta que en mi ejemplo anterior, el argumento some arguments
se analizaría como los dosseparadostrings "some
y arguments"
, que es una de las razones por las que nunca querrás almacenar un comando y sus argumentos en una sola cadena. También significa que detectaría el argumento name
dentro del argumento único "some name string"
, lo que sería un falso positivo.
Si la línea de comando está almacenada en una matriz (por ejemplo bash
), puede hacerlo así:
process=( cmd with "some arguments" and maybe name )
if [ "${process[0]}" != 'cmd' ]; then
echo 'The command is not "cmd"'
exit 1
fi
for arg in "${process[@]:1}"; do
if [ "$arg" = 'name' ]; then
echo 'Found "name" argument'
exit
fi
done
echo 'Did not find "name" argument in command line'
exit 1
La expansión de "${process[@]:1}"
sería toda la matriz pero no el primer elemento (el nombre del comando).
Para /bin/sh
(y dash
, pero también bash
y cualquier otro shell POSIX), lo anterior sería menos prolijo:
set -- cmd with "some arguments" and maybe name
if [ "$1" != 'cmd' ]; then
echo 'The command is not "cmd"'
exit 1
fi
shift
for arg do
if [ "$arg" = 'name' ]; then
echo 'Found "name" argument'
exit
fi
done
echo 'Did not find "name" argument in command line'
exit 1
Esto es esencialmente lo mismo que el primer fragmento de código, pero con el manejo correcto de todos los argumentos (citas, etc.) ya que nunca combinamos todos los elementos de la línea de comando en una sola cadena de texto.
Respuesta2
Como mencionaste bash, otra opción es su operador de coincidencia de patrones =~
:
if [[ $process =~ ^cmd[[:space:]].*name([[:space:]]|$) ]]
then
_name_ was found as an argument to _cmd_
fi
Esto busca ver si el contenido de la variable comienza con cmd
, seguido de un espacio, seguido de cualquier cosa o nada, seguido de name
, seguido de: un espacio o el final de la línea.