Quero usar grep
para verificar se um comando chamado cmd
que foi executado contém a palavra name
como argumento. (Eu sei sobre !*
o Bash, não quero usá-lo).
Digamos que eu tenha o comando em uma variável chamada process
.
No começo eu tentei echo $process | grep 'cmd name'
. Isso não é refinado o suficiente, pois leva em consideração apenas um argumento.
Então tentei ver se conseguia capturá-lo como último argumento usando o regex cmd .*(?= )name
. A ideia é capturar qualquer coisa, seguida de um espaço, seguida de name
(depois cmd
). Mas isso não funciona.
Meu objetivo, se isso funcionasse, era então tentar explicar o fato de ser um argumento em qualquer uma das posições.
Como posso usar expressões regulares para fazer isso?
Responder1
Na resposta abaixo, estou evitando explicitamente o uso de grep
. Uma linha de comando é uma lista de itens separados e deve ser analisada como tal, não como uma linha de texto.
Supondo que você mantenha o comando e os argumentos do comando em uma única string (seria melhor usar um array, vejaComo podemos executar um comando armazenado em uma variável?), então você pode fazer 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
Isso primeiro desabilitaria a geração de nome de arquivo, porque queremos usar $process
sem aspas para dividi-lo em palavras separadas, e se essa string contiver padrões globbing de nome de arquivo (como *
), isso atrapalharia nossa análise. Fazemos isso com set -f
.
Em seguida, definimos os parâmetros posicionais para as palavras em $process
. Depois disso, if "$1"
is cmd
, sabemos que devemos procurar name
no restante da linha de comando. Se não, paramos por aí.
Saímos da lista de parâmetros posicionais e começamos shift
a cmd
examinar os argumentos em um loop. Em cada iteração, simplesmente comparamos o argumento com a string name
. Se encontrarmos, dizemos e saímos.
No final do loop, sabemos que não encontramos o name
argumento, então reportamos isso e saímos com falha.
Observe que no meu exemplo acima, o argumento some arguments
seria analisado como os doisseparadostrings "some
e arguments"
, que é um dos motivos pelos quais você nunca deseja armazenar um comando e seus argumentos em uma única string. Isso também significa que detectaria o argumento name
dentro do único argumento "some name string"
, o que seria um falso positivo.
Se a linha de comando estiver armazenada em um array (por exemplo bash
), você poderia fazer assim:
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
A expansão de "${process[@]:1}"
seria todo o array, mas não o primeiro item (o nome do comando).
Para /bin/sh
(e dash
, mas também bash
e qualquer outro shell POSIX), o acima seria menos prolixo:
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
Isto é essencialmente igual ao primeiro trecho de código, mas com o tratamento correto de todos os argumentos (citações, etc.), uma vez que nunca combinamos todos os elementos da linha de comando em uma única string de texto.
Responder2
Já que você mencionou o bash, outra opção é o operador de correspondência de padrões =~
:
if [[ $process =~ ^cmd[[:space:]].*name([[:space:]]|$) ]]
then
_name_ was found as an argument to _cmd_
fi
Isso verifica se o conteúdo da variável começa com cmd
, seguido por um espaço, seguido por qualquer coisa ou nada, seguido por name
, seguido por: um espaço ou o final da linha.