Why does the following does not work?

INTERNAL_NUM=$(grep -E '\s*internal_num\s*=' file.xml |sed -E 's/internal_num\s*=\s*([0-9]\+)/\1/') 

I would like to print out the actual number but it prints:

internal_num = 1234 

The grep command prints internal_num = 1234 so that part is fine. It is the pipe to sed that does not work.

  • 4
    What is in file.xml? – jesse_b Mar 20 '18 at 14:26
  • @Jesse_b: The grep gives internal_num = 1234. So that part is fine. The pipe to sed does not work – Jim Mar 20 '18 at 14:32
  • @Jesse_b: echo "internal_num = 1234" | sed etc does not work – Jim Mar 20 '18 at 14:34
  • 1
    @Jim: Could you show us your actual file? Is it a proper XML formatted file or a text file? – Inian Mar 20 '18 at 14:40

If you have GNU grep, you could write

grep -oP '\binternal_num\s*=\s*\K\d+' file.xml

But you should not be parsing XML with regular expressions. Perhaps you need something like this

$ echo '
                  <wanted internal_num="1234" />
                  <wanted internal_num = "5678" />
  ' | xmlstarlet sel -t -v '//@internal_num' -n

Please show your input file.

  • While this shows another way of doing the same thing, it does not answer the question, "why does this not work [as expected]?". – Mark Perryman Mar 20 '18 at 14:39
  • Regex are not a solution for parsing html/xml – Gilles Quenot Mar 20 '18 at 14:40

Different tools and implementations/versions of a given tool support different regular expression syntax.

To be portable, you can limit yourself to the POSIX feature set:

sed -n "s/^\(.*$s\)\{0,1\}internal_num$s*=$s*\([0-9]\{1,\}\).*/\2/p"

(assuming there's only one occurrence per line)

If you know you'll only run it on systems where grep supports -o and -P (for perl-like regexps) à la GNU grep, you can do:

grep -Po '(?<!\S)internal_num\s*=\s*\K\d+'

You are using extended regex, but still escaping \+, therefore it looks for a literal plus sign and does not invoke the substitution.


INTERNAL_NUM=$(grep -E '\s*internal_num\s*=' file.xml |sed -E 's/internal_num\s*=\s*([0-9]+)/\1/') 

Testcases (tested on GNU sed V4.2.1):

$ echo "internal_num = 1234" | sed -E 's/internal_num\s*=\s*([0-9]\+)/\1/'
internal_num = 1234

$ echo "internal_num = 1234" | sed -E 's/internal_num\s*=\s*([0-9]+)/\1/'

As other answers mention (and explain in good detail), you should seriously consider not using regex to parse XML though.

  • The second snippet you pasted prints internal_num=1234 – Jim Mar 20 '18 at 15:08
  • It works with my GNU sed V4.2.1. So in that case you may also have to check other answers about portable sed options. E.g. replacing \s with [[:space:]] – Mark Perryman Mar 20 '18 at 15:12
  • Or maybe using sed -r not sed -E (which is undocumented). – Mark Perryman Mar 20 '18 at 15:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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