I have a problem with escaped pattern put in grep.

My test file is:

export_cc = ${dir}/aaa/bbb/ccc
export_cc = ${dir}/aaa/bbb/eee
export_cc = ${dir}/aaa/bbb/ddd
export_cc = ${dir}/aaa/bbb/fff
export_cc = ${dir}/aaa/bbb/ggg

If I run:

~/programming/sandbox $ printf %q 'export_cc = ${dir}/aaa/bbb/ccc'
export_cc\ =\ \$\{dir\}/aaa/bbb/ccc

and then copy output to grep as the pattern I would get:

~/programming/sandbox $ grep export_cc\ =\ \$\{dir\}/aaa/bbb/ccc file
export_cc = ${dir}/aaa/bbb/ccc

But if I put pattern into the variable or backticks I would get:

~/programming/sandbox $ grep `printf %q 'export_cc = ${dir}/aaa/bbb/ccc'` file
grep: Trailing backslash 

Could someone tell me why this makes the difference and how to use quoted string in variable as a grep pattern?

  • Show your desired output.
    – Cyrus
    Jun 1 '15 at 17:14
  • Desired output is the same as with putting pattern explicitly. When pattern is executed by expression in backticks grep fails and returns 2. Jun 1 '15 at 17:18

The problem was shell escaping. \ is special character in shell, and also in grep.


grep export_cc\ =\ \$\{dir\}/aaa/bbb/ccc file

export_cc\ =\ \$\{dir\}/aaa/bbb/ccc was interpreted by the shell to one string export_cc = ${dir}/aaa/bbb/ccc before passing to grep. You can use strace to check:

$ strace grep export_cc\ =\ \$\{dir\}/aaa/bbb/ccc file
execve("/bin/grep", ["grep", "export_cc = ${dir}/aaa/bbb/ccc",...

With Command Substitution:

grep `printf %q 'export_cc = ${dir}/aaa/bbb/ccc'` file

Without double quote, the result of Command Substitution was split and glob by the shell into three parts: export_cc\, =\, and \$\{dir\}/aaa/bbb/ccc. The shell treated them as three strings, escape all backslashes and pass them to grep as export_cc\\, =\\, \\$\\{dir\\}/aaa/bbb/ccc:

$ strace grep $(printf %q 'export_cc = ${dir}/aaa/bbb/ccc') file
execve("/bin/grep", ["grep", "export_cc\\", "=\\", "\\$\\{dir\\}/aaa/bbb/ccc",...

grep received a pattern with one backslash export_cc\ followed by nothing, and it shown the message Trailing backslash.

To make it works, you need to double quotes command substitution, and using printf with %s instead of %q:

$ grep "$(printf %s 'export_cc = ${dir}/aaa/bbb/ccc')" file
export_cc = ${dir}/aaa/bbb/ccc
  • thank you very much for detailed answer, especially for mentioning strace which will help me with debugging shell commands. Jun 1 '15 at 17:47

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.