vote up 3 vote down star

I often find this in scripts (and, I have to admit, write it myself):

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

or

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

Consider "foo" to include some regex stuff.

I feel that there should be - and most likely is - a better way to phrase this, one that does not involve two commands and a pipe but wraps the thing into some more compact expression.

I just can't find it. Anybody?

flag
i expect this expression is used frequently due to a combination of ignorance (not knowing alternatives) and maintainability (knowing alternatives but choosing this as the simpler to understand). i can tell at a glance what your examples do, but i need a shell reference to figure out the alternatives in grawity's and Dan McG's answers. – ~quack Feb 4 at 13:29
2  
By the way, the preferred method of doing command substitution is with $() rather than backticks. – Dennis Williamson Feb 4 at 15:47
2  
It is also a good idea to quote expansions so that whitespace is protected: a="$(echo "$x" | sed "s/foo/bar/")" and if echo "$x" | grep foo; …. – Chris Johnsen Feb 4 at 16:17
Good remarks on the $() vs. ``. I see my bash skills are not that great yet. – DevSolar Feb 4 at 16:44

3 Answers

vote up 2 vote down check

Unless you assume a specific shell, there is no better way to do this than “pipe echo to tool”. That is all that is all you can count on in the traditional Bourne shell and/or the POSIX shell. If you consider other shells, then there are some other built-in possibilities.

ksh has

  • extra patterns: ?(pattern-list), *(pattern-list), {n}(pattern-list), {n,m}(pattern-list), @(pattern-list), !(pattern-list);
  • the %P printf specifier to convert a extended regular expression into a pattern (and %R for extended regular expression to pattern);
  • the expr == pattern condition in [[ expr ]] tests;
  • the ${param/pattern/replacement} parameter expansion;

bash has

  • the extglob option to enable most of the extra patterns of ksh (no {n} and {n,m});
  • the expr == pattern condition (in [[ expr ]] tests);
  • the ${param/pattern/replacement} parameter expansion;
  • (in newer versions) the expr =~ extregexp condition (in [[ expr ]] tests) that can match against extended regular expressions
    • with parenthesized subexpressions and the BASH_REMATCH parameter, sed-style replacements could be done.

zsh has

  • its own extended patterns with the EXTENDED_GLOB option;
  • ksh-like extended patterns with the KSH_GLOB option;
  • the expr == pattern condition (in [[ expr ]] tests);
  • the ${pattern/pattern/replacement} parameter expansion;
  • the expr =~ extregexp condition (in [[ expr ]] tests), can use PCRE if the RE_MATCH_PCRE option is set
    • with parenthesized subexpressions and the MATCH and match parameters (or BASH_REMATCH with the BASE_REMATCH option set), sed-style replacements could be done;
  • the zsh/pcre module that offers pcre_compile, pcre_study, and pcre_match commands and the -pcre-match test condition (in [[ expr ]] tests);
  • the zsh/regex module that offers the -regex-match test condition (in [[ expr ]] tests);
link|flag
Wow. As complete as one could wish for. Definitely a winner. – DevSolar Feb 4 at 16:46
vote up 3 vote down

To replace the sed line, do something like

${a/foo/bar} or ${a//foo/bar}

In the first form, only the first instance is replaced. The second form is a global search & replace.

In your case, it would be

Instead of:

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

Consider using:

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

Where foo is a regular expression.

link|flag
In which shells does this work? – Peltier Feb 4 at 10:46
1  
if [ $x =~ foo ] gives an error message here. if [[ $x =~ foo ]], however, works great. Thanks! – DevSolar Feb 4 at 12:07
1  
All these are bashisms, they require bash. In addition, =~ requires bash >= 3 iirc. – Raphink Feb 4 at 13:35
1  
Iirc also, foo is not a regular expression (as in PCRE) in these examples, but uses wildcards. There's more of these bash goodness in the bash manpage, section "Parameter Expansion". I particularly appreciate things like ${a##foo} and ${a%%bar}. – Raphink Feb 4 at 13:38
2  
The match operator =~ does use regular expressions. This is true, for example: [[ "abcdddde" =~ ^a.*d+.g* ]] (that's zero-or-more g's rather than g-wildcard, for example). – Dennis Williamson Feb 4 at 15:43
show 4 more comments
vote up 2 vote down
sed 's/foo/bar/' <<< "foobie"

grep 'this' <<< "this or that"

While Dan McG's answers are good, they won't give exactly the same regexp support if you need it.

link|flag
1  
I'm not sure if I like <<< better than the echo-pipe, but I didn't know about <<< before. +1, and thanks. – DevSolar Feb 4 at 13:15
1  
That's a "here string". There's also a multi-line version called a "here document". – Dennis Williamson Feb 4 at 15:46

Your Answer

Get an OpenID
or
never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.