Melhor maneira de fazer “eco $ x | sed… ”e“ eco $ x | grep ... "

14

Costumo encontrar isso em scripts (e, devo admitir, escrevo eu mesmo):

a=$(echo "$x" | sed "s/foo/bar/")

ou

if echo "$x" | grep -q foo
then
    ...
fi

Considere "foo" para incluir algumas coisas de expressões regulares.

Eu sinto que deveria haver - e provavelmente existe - uma maneira melhor de expressar isso, uma que não envolva dois comandos e um cachimbo, mas envolva a coisa em uma expressão mais compacta.

Eu simplesmente não consigo encontrá-lo. Qualquer pessoa?

DevSolar
fonte
Espero que essa expressão seja usada com frequência devido a uma combinação de ignorância (não conhecendo alternativas) e manutenibilidade (conhecendo alternativas, mas escolhendo essa como a mais simples de entender). posso dizer rapidamente o que seus exemplos fazem, mas preciso de uma referência de shell para descobrir as alternativas nas respostas de grawity e Dan McG.
quack quixote
3
A propósito, o método preferido de substituição de comando é com $()backticks.
Pausado até novo aviso.
2
Também é uma boa idéia citar expansões para que os espaços em branco sejam protegidos: a="$(echo "$x" | sed "s/foo/bar/")"e if echo "$x" | grep foo; ….
Chris Johnsen
Boas observações sobre $ () vs. ``. Vejo que minhas habilidades no bash ainda não são boas.
DevSolar 04/04

Respostas:

12

A menos que você assuma um shell específico, não há maneira melhor de fazer isso do que "eco de tubo para ferramenta" (ou apenas uma "ferramenta" como expr ); é tudo o que você pode contar com o tradicional shell Bourne e / ou o POSIX . Se você considerar outras conchas, existem outras possibilidades integradas.

ksh tem

  • padrões extras: ?(pattern-list), *(pattern-list), {n}(pattern-list), {n,m}(pattern-list), @(pattern-list), !(pattern-list),
  • o especificador %P printf para converter uma expressão regular estendida em um padrão (e %Rpara expressão regular estendida em padrão);
  • a expr == patterncondição em [[ expr ]]testes;
  • a ${param/pattern/replacement}expansão do parâmetro.

bash tem

  • a extglobopção de habilitar a maioria dos padrões extras do ksh (no {n}e {n,m});
  • a expr == patterncondição (em [[ expr ]]testes);
  • a ${param/pattern/replacement}expansão do parâmetro;
  • (nas versões mais recentes) a expr =~ extregexpcondição (nos [[ expr ]]testes) que pode corresponder às expressões regulares estendidas
    • com subexpressões entre parênteses e o BASH_REMATCHparâmetro, substituições no estilo sed podem ser feitas.

zsh tem

  • seus próprios padrões estendidos com a EXTENDED_GLOBopção;
  • padrões estendidos do tipo ksh com a KSH_GLOBopção;
  • a expr == patterncondição (em [[ expr ]]testes);
  • a ${pattern/pattern/replacement}expansão do parâmetro;
  • a expr =~ extregexpcondição (em [[ expr ]]testes) que pode corresponder a expressões regulares estendidas,
    • ele pode usar PCRE em vez de expressões regulares estendidas simples se a opção RE_MATCH_PCRE estiver configurada,
    • com subexpressões entre parênteses, o MATCHparâmetro e o matchparâmetro (ou BASH_REMATCHcom o BASH_REMATCHconjunto de opções), podem ser feitas substituições no estilo sed ;
  • o zsh/pcremódulo que ofertas pcre_compile, pcre_study, e pcre_matchcomandos e o -pcre-match teste de condição (em [[ expr ]]testes);
  • o zsh/regexmódulo que oferece a condição de -regex-match teste (em [[ expr ]]testes).
Chris Johnsen
fonte
Uau. Tão completo quanto se poderia desejar. Definitivamente um vencedor.
DevSolar 04/02
6

Para substituir a linha sed, faça algo como

${a/foo/bar} ou ${a//foo/bar}

Na primeira forma, apenas a primeira instância é substituída. O segundo formulário é uma pesquisa e substituição global.

No seu caso, seria

Ao invés de:

if echo $x | grep foo
then
    ...
fi

Considere usar:

if [ $x =~ foo ]
then
    ...
fi

Onde fooestá uma expressão regular.

Dan McGrath
fonte
Em que conchas isso funciona?
Peltier
1
if [ $x =~ foo ]dá uma mensagem de erro aqui. if [[ $x =~ foo ]], no entanto, funciona muito bem. Obrigado!
DevSolar 04/02/10
1
Todos estes são bashismos, eles exigem bash. Além disso, =~requer bash> = 3 iirc.
precisa saber é o seguinte
1
Iirc também, foo não é uma expressão regular (como no PCRE) nesses exemplos, mas usa caracteres curinga. Há mais dessas vantagens do bash na página de manual do bash, seção "Expansão de parâmetros". Eu particularmente aprecio coisas como ${a##foo}e ${a%%bar}.
precisa saber é o seguinte
3
O operador de correspondência =~usa expressões regulares. Isso é verdade, por exemplo: [[ "abcdddde" =~ ^a.*d+.g* ]](isso é zero ou mais gs em vez de g-curinga, por exemplo).
Pausado até novo aviso.
2

Uma boa maneira compatível com posix para testar se uma variável contém um padrão é:

test ${var##*foo*} || <do something>;

A sintaxe da expansão do parâmetro é:

 ${parameter##pattern}

Onde patterné um padrão de concha .

mrucci
fonte