Meilleure façon de faire “echo $ x | sed… ”et“ echo $ x | grep… ”

14

Je trouve souvent cela dans les scripts (et, je dois l'admettre, je l'écris moi-même):

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

ou

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

Considérez "foo" pour inclure des trucs regex.

Je pense qu'il devrait y avoir - et très probablement est - une meilleure façon de formuler ce, qui ne comporte pas deux commandes et un tuyau , mais enveloppe la chose dans une expression plus compacte.

Je ne le trouve pas. N'importe qui?

DevSolar
la source
Je m'attends à ce que cette expression soit fréquemment utilisée en raison d'une combinaison d'ignorance (ne pas connaître d'alternatives) et de maintenabilité (connaître des alternatives mais choisir celle-ci comme la plus simple à comprendre). Je peux dire en un coup d'œil ce que font vos exemples, mais j'ai besoin d'une référence shell pour trouver les alternatives dans les réponses de grawity et Dan McG.
Quack Quichote
3
Soit dit en passant, la méthode préférée pour effectuer la substitution de commandes est $()plutôt que les raccourcis.
pause jusqu'à nouvel ordre.
2
C'est aussi une bonne idée de citer des extensions pour que les espaces blancs soient protégés: a="$(echo "$x" | sed "s/foo/bar/")"et if echo "$x" | grep foo; ….
Chris Johnsen
Bonnes remarques sur le $ () vs ``. Je vois que mes compétences de bash ne sont pas encore géniales.
DevSolar

Réponses:

12

À moins que vous ne supposiez un shell spécifique, il n'y a pas de meilleur moyen de le faire que de "faire écho à l'outil" (ou simplement un "outil" lui-même comme expr ); c'est tout ce sur quoi vous pouvez vraiment compter avec le shell Bourne traditionnel et / ou le shell POSIX . Si vous envisagez d'autres coquilles, il existe d'autres possibilités intégrées.

ksh a

  • modèles supplémentaires: ?(pattern-list), *(pattern-list), {n}(pattern-list), {n,m}(pattern-list), @(pattern-list), !(pattern-list);
  • le spécificateur %P printf pour convertir une expression régulière étendue en motif (et %Rpour l'expression régulière étendue en motif);
  • l' expr == patternétat dans les [[ expr ]]tests;
  • l' ${param/pattern/replacement}expansion des paramètres.

bash a

  • l' extgloboption d'activer la plupart des modèles supplémentaires de ksh (non {n}et {n,m});
  • l' expr == patternétat (lors des [[ expr ]]tests);
  • l' ${param/pattern/replacement}expansion des paramètres;
  • (dans les versions plus récentes) la expr =~ extregexpcondition (dans les [[ expr ]]tests) qui peut correspondre aux expressions régulières étendues
    • avec des sous-expressions entre parenthèses et le BASH_REMATCHparamètre, des remplacements de style sed pourraient être effectués.

zsh a

  • ses propres modèles étendus avec l' EXTENDED_GLOBoption;
  • modèles étendus de type ksh avec l' KSH_GLOBoption;
  • l' expr == patternétat (lors des [[ expr ]]tests);
  • l' ${pattern/pattern/replacement}expansion des paramètres;
  • la expr =~ extregexpcondition (dans les [[ expr ]]tests) qui peut correspondre à des expressions régulières étendues,
    • il peut utiliser PCRE au lieu d'expressions régulières étendues simples si l'option RE_MATCH_PCRE est définie,
    • avec des sous-expressions entre parenthèses, le MATCHparamètre et le matchparamètre (ou BASH_REMATCHavec l' BASH_REMATCHensemble d'options), des remplacements de style sed pourraient être effectués;
  • le zsh/pcremodule qui offre pcre_compile, pcre_studyet les pcre_matchcommandes et la condition de -pcre-match test (dans les [[ expr ]]tests);
  • le zsh/regexmodule qui offre la condition de -regex-match test (en [[ expr ]]tests).
Chris Johnsen
la source
Sensationnel. Aussi complet que l'on pourrait souhaiter. Certainement un gagnant.
DevSolar
6

Pour remplacer la ligne sed, faites quelque chose comme

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

Dans le premier formulaire, seule la première instance est remplacée. Le deuxième formulaire est une recherche globale et remplacer.

Dans votre cas, ce serait

Au lieu de:

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

Pensez à utiliser:

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

fooest une expression régulière.

Dan McGrath
la source
Dans quels obus cela fonctionne-t-il?
Peltier
1
if [ $x =~ foo ]donne un message d'erreur ici. if [[ $x =~ foo ]], cependant, fonctionne très bien. Merci!
DevSolar
1
Ce sont tous des bashismes, ils ont besoin de bash. De plus, =~nécessite bash> = 3 iirc.
ℝaphink
1
Iirc également, foo n'est pas une expression régulière (comme dans PCRE) dans ces exemples, mais utilise des caractères génériques. Il y a plus de ces qualités bash dans la page de manuel bash, section "Expansion des paramètres". J'apprécie particulièrement des choses comme ${a##foo}et ${a%%bar}.
ℝaphink
3
L'opérateur de =~correspondance utilise des expressions régulières. C'est vrai, par exemple: [[ "abcdddde" =~ ^a.*d+.g* ]](c'est zéro ou plus de g plutôt que g-joker, par exemple).
pause jusqu'à nouvel ordre.
2

Un bon moyen compatible posix pour tester si une variable contient un motif est:

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

La syntaxe de l' expansion des paramètres est la suivante:

 ${parameter##pattern}

patternest un motif de coquille .

mrucci
la source