Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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?

share|improve this question
    
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 quixote Feb 4 '10 at 13:29
3  
By the way, the preferred method of doing command substitution is with $() rather than backticks. –  Dennis Williamson Feb 4 '10 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 '10 at 16:17
    
Good remarks on the $() vs. ``. I see my bash skills are not that great yet. –  DevSolar Feb 4 '10 at 16:44

4 Answers 4

up vote 10 down vote accepted

Unless you assume a specific shell, there is no better way to do this than “pipe echo to tool” (or just a “tool” itself like expr); it is all you can really count on with 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) that can match against extended regular expressions,
    • it can use PCRE instead of plain extended regular expressions if the RE_MATCH_PCRE option is set,
    • with parenthesized subexpressions, the MATCH parameter, and the match parameter (or BASH_REMATCH with the BASH_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).
share|improve this answer
    
Wow. As complete as one could wish for. Definitely a winner. –  DevSolar Feb 4 '10 at 16:46

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.

share|improve this answer
    
In which shells does this work? –  Peltier Feb 4 '10 at 10:46
1  
if [ $x =~ foo ] gives an error message here. if [[ $x =~ foo ]], however, works great. Thanks! –  DevSolar Feb 4 '10 at 12:07
1  
All these are bashisms, they require bash. In addition, =~ requires bash >= 3 iirc. –  ℝaphink Feb 4 '10 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}. –  ℝaphink Feb 4 '10 at 13:38
3  
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 '10 at 15:43
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.

share|improve this answer
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 '10 at 13:15
2  
That's a "here string". There's also a multi-line version called a "here document". –  Dennis Williamson Feb 4 '10 at 15:46

A good posix compatible way for testing if a variable contains a pattern is:

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

The syntax of the parameter expansion is:

 ${parameter##pattern}

where pattern is a shell pattern.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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