Can’t pipe or redirect Cygwin grep output

batchcygwin;grep

How do I get Cygwin's grep to work properly in a regular cmd.exe?

> grep -o 'ProductVersion\".*\".*\"' foo.txt | grep -o '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'
foo.txt:ProductVersion" Value="59.59.140.59"
grep: |: No such file or directory
grep: grep: No such file or directory
grep: [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+: No such file or directory

and

> grep -o 'ProductVersion\".*\".*\"' foo.txt >> blah.txt
foo.txt:ProductVersion" Value="59.59.140.59"
grep: >>: No such file or directory
grep: blah.txt: No such file or directory

Will gladly accept someone else's answer, but modifying my command to not use escaped quotes solved my issue. Thanks, @barlop.

In my particular search, I was able to change

grep -o 'ProductVersion\".*\".*\"' foo.txt >> blah.txt

to

grep -o 'ProductVersion.*Value.*' foo.txt | grep -v Name >> blah.txt

I would call this more of a workaround.

Best Answer

For Cygwin's grep

A workaround, is that you can specify the ASCII value in Bash. " is 22 in hex.

Two points: You have to remove the single quotes from around the first part, so that $'\x22' is interpreted as special, not as literal.

And for the second part of the expression you can't just use -o, it has to be -oE.

Because + is part of ERE, and without -E, it's just BRE. It thinks + is literal.

Proof + is literal there.. 55.55.55.55 won't match but this will:

$ echo 3+.3+.3+.3+ | grep -o [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
3+.3+.3+.3+

So here's the line you had but adjusted..

Using Bash's feature of expanding ASCII codes, instead of using quotes. Removing quotes from around the first part, and adding -E to second part:

$ grep -o ProductVersion$'\x22'.*$'\x22'.*$'\x22' foo.txt | grep -oE [0-9]+\.[0
-9]+\.[0-9]+\.[0-9]+
59.59.140.59

ADDED

If you replace [0-9]+ with [0-9][0-9]* (which is the same), then you can use grep without the -E.

You can use grep -P and then you can use \d for [0-9], but you have to use quotes around the second part. Or \\d.

In fact, here is a great solution that totally solves your original problem.. You only need a quote around the problematic bit. (By the way, I could make the regular expression in the second half more efficient using the repetition operator, but that's not relevant to the issue we've had with quotes which I'm focussing on).

This works. Dropping the single quotes from the first bit, and using \" to make them literal quotes. This gets round the bug of the double quotes needing to be single quoted. (Weird bug if Windows NT's findstr has something like it, though not with single quotes no doubt.)

grep -P in the second part, allows us to use \d. We could put quotes around the regular expression in the second half. Or, we can just put quotes around the '\d\ or, we can do as I have done and use \\d. (\d alone -unescaped and unquoted, won't match because it gets interpreted by Bash and reduced to d when grep gets it.)

$ grep -o ProductVersion\".*\".*\" foo.txt | grep -oP \\d+\.[0-9]+\.[0-9\]+\.[0
-9]+
59.59.140.59

Now that we've dealt with the quotes issue, I'll make it more efficient with the repetition operator. The regular expression of 3{4} means 3333. The regular expression of (fg){4} would mean fgfgfgfg.

$ grep -o ProductVersion\".*\".*\" foo.txt | grep -P '(\d.){4}'
ProductVersion" Value="59.59.140.59""

$ grep -o ProductVersion\".*\".*\" foo.txt | grep -P '('\\d.')'{4}
ProductVersion" Value="59.59.140.59""

$ grep -o ProductVersion'"'.*'"'.*'"' foo.txt | grep -P '('\\d.')'{4}
ProductVersion" Value="59.59.140.59""
Related Question