O livro que estou lendo - Learning the Bash Shell de O'Reilly especifica alguns códigos da seguinte forma:
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
howmany=$1
shift
....
....
etc
Isso usa o
grep
utilitário de pesquisa para testar se$1
corresponde ao padrão apropriado. Para fazer isso, fornecemos a expressão regular^-[0-9][0-9]*$
ao grep, que é interpretada como "um travessão inicial seguido por um dígito, opcionalmente seguido por um ou mais dígitos". Se uma correspondência for encontrada,grep
a correspondência será retornada e o teste será verdadeiro; caso contrário,grep
não retornará nada e o processamento passará para oelif
teste.Observe que colocamos a expressão regular entre aspas simples para impedir que o shell interprete $ e * e os transmita para grep sem modificação.
Então, por que a expressão regular não '^-[0-9]'
perde seu significado como entre aspas simples, geralmente tudo entre aspas simples perde seu significado.
Obrigado pela ajuda.
Responder1
Embora outros tenham respondido à sua pergunta específica, acrescentarei que
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
É uma maneira errada de verificar se uma string corresponde a uma expressão regular por vários motivos:
- Você não pode usar
echo
para dados arbitrários - Deixar uma expansão de parâmetro sem aspas como
$1
acima é o operador split+glob. grep
não corresponde ao regex com sua entrada completa, mas em cada linha de sua entrada. Portanto, retornaria verdadeiro,foo\n-0\nbar
por exemplo.- Um regexp pode corresponder a comprimento zero, portanto, no caso geral, é errado verificar se
grep
produz alguma saída (observe que a substituição do comando tira os caracteres de nova linha à direita). É melhor usargrep -q
e confiar no status de saída degrep
, em vez de[
, e também evitar a substituição de comando. - Observe que esse
grep
comando pode ser simplificado paragrep -xE '-[0-9]+'
bash
e ksh93
tem zsh
um operador dedicado para correspondência de regexp (estendida). Para usá-lo de forma portável e confiável em todos os três (e no bash-3.1), a sintaxe é:
re='^-[0-9]+$'
if [[ $1 =~ $re ]]; then
echo matches
fi
yash
e zsh
também suporte:
if [ "$1" '=~' '^-[0-9]+$' ]; then
echo matches
fi
O comando padrão para fazer a correspondência de regex de string (básica) é expr
:
if expr " $1" : ' -[0-9]\{1,\}$' > /dev/null; then
echo matches
fi
Observe que o ^
(mas não $
) está implícito em expr
. Também usamos esse caractere de espaço inicial para evitar problemas com valores que $1
sejam expr
operadores.
Observe também que se a regex contiver \(...\)
, isso afetará o comportamento de expr
.
Resumindo, é melhor usar, awk
que é outra maneira padrão/portátil de fazer isso (observe que awk
usa regexps estendidos):
if STRING=$1 RE='^-[0-9]+$' awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'; then
...
Ou use uma função:
re_match() {
STRING=$1 RE=$2 awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'
}
if re_match "$1" '^-[0-9]+$'
Nesse caso, você também pode conseguir isso com uma case
construção padrão:
case $1 in
("" | *[!0-9-]* | [!-]* | - | ?*-*) ;;
(*) echo match;;
esac
Para usar grep
, você pode usá-lo com a --null
opção (quando compatível, pois não é uma opção padrão) para fazê-lo funcionar em registros delimitados por NUL em vez de registros delimitados por nova linha. Como na maioria dos shells $1
não pode conter NULs, isso deve ser seguro:
if printf %s "$1" | grep --null -xq '-[0-9]+$'; then
echo match
fi
Responder2
Aspas simples dizem ao shell para manter os caracteres incluídos como estão,sem qualquer interpretação. A string citada é passada como está para grep
, sem as aspas: quando grep
procura seus argumentos, ela vê
grep
e
^-[0-9][0-9]*$
e age sobre isso. (LerComo os programas são executadosse você estiver curioso sobre a construção de argumentos no Linux.)
bash
e grep
são diferentes. A forma como este comando usa aspas garante que bash
não processe a string, mas grep
sim.
Responder3
Aspas simples evitamglobulando(permitindo bash
interpretar curingas como *
) e expansão de variáveis através do uso de $
. Basicamente, você está dizendo para bash
"pegar literalmente esses caracteres e passá-los para grep
". Quando grep
os vê, ele é construído para entender expressões regulares, entãoentãoo regexp é interpretado dentro grep
.
Versão mais curta: argumentos com aspas simples fornecem um meio de escapar do processamento do seu shell antes que o argumento seja passado para o comando.
Responder4
Perde o sentido. grep
usa quase os mesmos padrões de regex do bash.