For the unquoted example, each \\
pair passes one backslash to grep, so 4 backslashes pass two to grep, which translates to a single backslash. 6 backslashes pass three to grep, translating to one backslash and one \c
, which is equal to c
. One additional backslash does not change anything, because it is translated \c
-> c
by the shell. Eight backslashes in the shell are four in grep, translated to two, so this does not match anymore.
For the example in double quotes, note what follows your second quote from the bash manpage:
The backslash retains its
special meaning only when followed by one of the following characters:
$, `, ", \, or newline.
I.e. when you give an odd number of backslashes, the sequence ends in \c
, which would be equal to c
in the unquoted case, but when quoted, the backslash looses its special meaning, so \c
is passed to grep. That is why the range of "possible" backslashes (i.e. those that make up a pattern matching your example file) slides down by one.
You could do the following using some implementations of find
and xargs
like this.
$ find . -type f -print0 | xargs -r0 ./myscript
or, standardly, just find
:
$ find . -type f -exec ./myscript {} +
Example
Say I have the following sample directory.
$ tree
.
|-- dir1
| `-- a\ file1.txt
|-- dir2
| `-- a\ file2.txt
|-- dir3
| `-- a\ file3.txt
`-- myscript
3 directories, 4 files
Now let's say I have this for ./myscript
.
#!/bin/bash
for i in "$@"; do
echo "file: $i"
done
Now when I run the following command.
$ find . -type f -print0 | xargs -r0 ./myscript
file: ./dir2/a file2.txt
file: ./dir3/a file3.txt
file: ./dir1/a file1.txt
file: ./myscript
Or when I use the 2nd form like so:
$ find . -type f -exec ./myscript {} +
file: ./dir2/a file2.txt
file: ./dir3/a file3.txt
file: ./dir1/a file1.txt
file: ./myscript
Details
find + xargs
The above 2 methods, though looking different, are essentially the same. The first is taking the output from find, splitting it using NULLs (\0
) via the -print0
switch to find. The xargs -0
is specifically designed to take input that's split using NULLs. That non-standard syntax was introduced by GNU find
and xargs
but is also found nowadays in a few others like most recent BSDs. The -r
option is required to avoid calling myscript
if find
finds nothing with GNU find
but not with BSDs.
NOTE: This entire approach hinges on the fact that you'll never pass a string that's exceedingly long. If it is, then a 2nd invocation of ./myscript
will get kicked off with the remainder of subsequent results from find.
find with +
That's the standard way (though it was only added relatively recently (2005) to the GNU implementation of find
). The ability to do what we're doing with xargs
is literally built into find
. So find
will find a list of files and then pass that list as as many arguments as can fit to the command specified after -exec
(note that {}
can only be last just before +
in this case), running the commands several times if needed.
Why no quoting?
In the first example we're taking a shortcut by completely avoiding the issues with the quoting, by using NULLs to separate the arguments. When xargs
is given this list it's instructed to split on the NULLs effectively protecting our individual command atoms.
In the second example we're keeping the results internal to find
and so it knows what each file atom is, and will guarantee to handle them appropriately, thereby avoiding the whoie business of quoting them.
Maximum size of command line?
This question comes up from time to time so as a bonus I'm adding it to this answer, mainly so I can find it in the future. You can use xargs
to see what the environment's limit like so:
$ xargs --show-limits
Your environment variables take up 4791 bytes
POSIX upper limit on argument length (this system): 2090313
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 2085522
Size of command buffer we are actually using: 131072
Best Answer
The backquotes mean command substitution: the result of expanding
`pidof transmission-gtk`
is the output from running the commandpidof transmission-gtk
. An alternate way of writing`pidof transmission-gtk`
is$(pidof transmission-gtk)
, and that alternate way is recommended (it has no downsides and it avoids issues when the command inside the backquotes itself contains quotes).The double quotes around that prevent the result of the command substitution from being split into separate words and interpreted as wildcard patterns. If you're just starting with shell scripting, don't worry about this, just remember to always put double quotes around command substitutions. The same goes for variable substitutions, e.g. always write
"$foo"
, never plain$foo
.This snippet as written doesn't make any sense: the test contains a single word, which will be something like
=0
(if there is no runningtransmission-gtk
process) or1234=0
(if there is a matching process) or123 456=0
(a single word in shell terminology, even if it happens to contain a space). A single word in a test is always considered true. To test whether a string is equal to another string, write[ "string1" = "string2" ]
(there must be a space on each side of the brackets, and on each side of the operator). To test for numerical equality, use the-eq
operator, e.g.[ 1 -eq 01 ]
is true.Here, there are two equivalent ways of testing whether there is a running
transmission-gtk
process:Test whether the output is empty:
Test whether the return status of the
pidof
is nonzero, indicating failure. Since the output of the command is not used, redirect it to/dev/null
(the great void).