9

I don't remember the exact commands and tricks that I use sometimes which solves much of the work, so I try to log them into a file for future reference. What I typically do is just put whole command in echo and append it to the file quickly.

This has always worked, but the following situation arrived for first time, where I was not able to do as stated above.

Successfull logging

$ echo "cat file1.txt | paste -d, - - ## Step 3 " >> ~/globalLog.txt

Got stuck here

$ echo "cat file2.txt  | sed 's/"//g' > file3.txt ## Step 2 " >> ~/globalLog.txt

The above is giving a obvious error, as the quotes are not being properly closed (dropping to the second command propmt (PS2 I guess?) for completing the command i.e. >_ in my case), the single(') quotes are being used by the sed command and the double(") quotes are being used in one the sed expression for replacement.

How would I enclose the complete command in such situation ?

mtk
  • 27,530
  • 35
  • 94
  • 130
  • It looks like you're trying to log a command before executing it. Are you aware of set -v and set -x? – derobert Oct 24 '12 at 20:16

5 Answers5

13

You can entirely avoid the need to quote using here documents. Note that setting the label in single/double quotes(as in "EOF" in the example below) disables variable and command evaluation within the text.

cat <<"EOF" >>~/globalLog.txt
cat file2.txt  | sed 's/"//g' > file3.txt ## Step 2
EOF
iruvar
  • 16,725
7

You have many choices, and each of them can be convenient in different situations.

  1. Double quote the whole string as one, and escape the literal double quotes inside (easy to read if you have only one such occurrence, but you could end up with a picket fence):

    echo "cat file2.txt  | sed 's/\"//g' > file3.txt ## Step 2 " >> ~/globalLog.txt
    
  2. Use different quotes for the different parts of the string:

    echo "cat file2.txt  | sed 's/"'"'"//g' > file3.txt ## Step 2 " >> ~/globalLog.txt
    
  3. Use echo's ability to process multiple arguments. This is applicable if only a single word contains the offending character, since the result will typically have a space between each argument, so it won't work for your string. Alternative example:

    echo 'foo"' "'bar"
    

    prints

    foo" 'bar
    
  4. Use multiple echo -n statements (or printf %s) followed by an echo without -n:

    echo -n "cat file2.txt  | sed '" >> ~/globalLog.txt
    echo -n 's/"' >> ~/globalLog.txt
    echo "//g' > file3.txt ## Step 2 " >> ~/globalLog.txt
    
  5. Don't use quotes at all, but escape all special characters:

    echo cat\ file2.txt\ \ \|\ sed\ \'s/\"//g\'\ \>\ file3.txt\ \#\#\ Step\ 2\  >> ~/globalLog.txt
    

Note that it's not possible to escape single quotes in a single quoted string.

And if in doubt, Use More Quotes™.

In your case, however, it looks like you're logging a command before executing it. There's already a very nice solution for working with commands as text.

l0b0
  • 51,350
  • 1
    My usual method (which is the simplest for programmatically quoting unknown data) is to enclose the whole thing in single quotes, then break out and backslash escape any that are inside. e.g. 'don'\''t' - this is similar to your "..."'"'"..." example but slightly easier to follow. – Random832 Oct 24 '12 at 17:06
6

There is a simple, systematic and POSIX-compliant way to quote any string. You only need to remember two rules:

  1. Quote apostrophes (single quotes) with a backslash like so: \'
  2. Quote everything other than apostrophes by surrounding it with apostrophes like so: 'printf "foo\n"'

To illustrate how to use these rules together, the word don't can be systematically quoted like this: 'don'\''t'

You can often find a more readable way of quoting strings, but when things get complicated, this is the reliable way to get the quoting you want.

The quoted form of the string in the question is:

$ echo 'cat file2.txt  | sed '\''s/"//g'\'' > file3.txt ## Step 2 ' >> ~/globalLog.txt
aecolley
  • 2,177
3

just escape the " like so:

echo "cat file2.txt  | sed 's/\"//g' > file3.txt ## Step 2 " >> ~/globalLog.txt
h3rrmiller
  • 13,235
1

Just

$ cat >> ~/globalLog.txt
<now type your command>
echo "cat file2.txt  | sed 's/"//g' > file3.txt ## Step 2 "
<hit ctrl-d>
Useless
  • 4,800