How can I pass strings with single quotes to grep?

10,848

Solution 1

Single quotes are terminated by single quotes; all other characters in between are preserved exactly as is, including backslashes. Thus there is no way to embed a single quote between single quotes. (But you can end the single quotes, escape a single quote, and start a new set of single quotes, as in 'Single quotes aren'\''t ever really embedded in single quotes.')

Suggestion: Avoid find+xargs when grep -r pattern . can recursively grep on the current directory.

The below commands have equivalent behavior:

grep -rns "add_action('save_post'," .
grep -rns 'add_action('\'save_post\', .

The last command is interpreted as:

  1. 'add_action(' -> add_action(
  2. \' -> '
  3. save_post -> save_post
  4. \' -> '
  5. , -> ,

Concatenating these parts, the grep command receives the argument add_action('save_post',.

Solution 2

xargs expects arguments quoted in some strange way that find doesn't produce. Never use xargs in combination with find, unless you know that your file names don't contain \"' or whitespace.

Instead of using xargs, let find directly call the program you want to run.

find . -exec grep -ns 'add_action('\''save_post'\'',' {} +

With exec … {} +, the program is invoked once for many files at once, like with xargs. Some older versions of find don't support + here¹, then you have to use ; instead, which invokes grep once per file.

You can alternatively use find … -print0 | xargs -0, if your utilities support it¹. The -print0 option tells find to emit names separated by a null byte, and -0 tells xargs to expect null-separated input and not to do any quote interpretation.

Note the quoting above: between single quotes, all characters are interpreted literally, except that ' signifies the end of the literal string. So '\'' is effectively a way of putting a literal single quote inside a single-quoted string; formally, it's “' end quote, \ literal ' single quote, ' begin quote”.

In the special case of grep, if your implementation supports it¹, you can dispense with find and use grep -r to search inside a directory recursively:

grep -r 'add_action('\''save_post'\'',' .

¹ Linux, Cygwin, FreeBSD and OSX support these features.

Solution 3

grep "add_action('save_post'," ./* -R
  1. You can grep all files in current directory recursively with -R option.

  2. Search string can be surrounded with ".

Share:
10,848

Related videos on Youtube

Matt
Author by

Matt

Programs computers. Likes bikes. Plays guitar. Learns languages.

Updated on September 18, 2022

Comments

  • Matt
    Matt over 1 year

    My desired outcome is the following: to recursively search a directory looking for a given string in all found files. The following command is my usual port of call:

    find ./ | xargs grep -ns 'foobar'
    

    However, when foobar has quotes the command fails and gives me a > prompt in the shell. The specific command that's causing the problem is as follows:

    find ./ | xargs grep -ns 'add_action(\'save_post\','
    

    I've tried to escape the quotes with backslashes but to no avail. What's the correct way to do this?

  • Matt
    Matt about 12 years
    great insight on the interpretation, thanks!
  • Matt
    Matt about 12 years
    Thanks for the info about -print0 and -0, I've looked at these options before but never understood them well enough to actually yield the results I needed. I'll be using grep -rns from now on.