Кавычки

Кавычки, обрамляющие строку, предотвращают интерпретацию специальных символов, которые могут находиться в строке. Символ называется "специальным", если он не только означает самого себя но и имеет дополнительное значение для программ, например символ шаблона -- *.
В этом примере, символ "*", интерпретируется как шаблон:

freebsd82 /work-tests/sh# ll [Bb]* # ll - в данном случае псевдоним для команды ls -lASo
-rw-r--r--  1 root  wheel  - 0 Apr 27 12:44 BOOBOO.TXT
-rw-r--r--  1 root  wheel  - 0 Apr 27 12:44 booboo.txt
-rw-r--r--  1 root  wheel  - 0 Apr 27 12:45 brrr

А тут просто как символ "звездочка", естественно команда ls, не находит такого файла:

freebsd82 /work-tests/sh# ll '[Bb]*' # одинарные кавычки
ls: [Bb]*: No such file or directory
freebsd82 /work-tests/sh# ll "[Bb]*" # двойные кавычки
ls: [Bb]*: No such file or directory

Имейте в виду, многие программы в передаваемых им параметрах, используют специальные символы, в этом случае нужно заключать их в кавычки, что-бы системная оболочка их не трогала, оставляя для вызываемой программы.
В примере ниже, производится поиск в файлах, начинающихся на символы B или b в текущем рабочем каталоге, строк, начинающихся с символов T или t. Команда grep, получает в качестве параметра шаблон, если не заключить его в кавычки, он будет интерпретирован оболочкой, до того как попадет к grep, и ничего найдено не будет:

freebsd82 /work-tests/sh# grep '[Tt]*' ./[Bb]*
./BOOBOO.TXT:Test line first
./BOOBOO.TXT:test line second
./booboo.txt:test line too
./brrr:mmm.. test it

При обращении к переменным, желательно использовать двойные кавычки. Это позволит не интерпретировать специальные символы, содержащиеся в именах переменных, за исключением символов $, ` ( обратная кавычка) и \ ( обратный слэш ). То что символ $ является исключением, позволяет производить подстановку переменных в строке , то есть сценарий:

  1. #!/usr/local/bin/bash
  2.  
  3. var="variable"
  4. echo "This is $var"

выведет:

freebsd82 /# ./test.sh
This is variable

Кроме вышесказанного, двойные кавычки используются для предотвращения разбиения строки на слова:

  1. #!/usr/local/bin/bash
  2.  
  3. var="is a variable"
  4. echo this $var # здесь команде передается  4 аргумента
  5. echo "this $var"  # а тут один

Результат обеих команд echo, будет одинаков, но только на первый взгляд:

freebsd82 /work-tests/sh# ./test.sh
this is a variable # здесь на выходе мы получаем 4 слова
this is a variable # а здесь одну строку

Заключать в кавычки аргументы команды echo, нужно только если разбиение вывода на слова, вызывает какие-то трудности.

Пример вывода необычных переменных:

  1. #!/usr/local/bin/bash
  2.  
  3. var="'(]\\{}\$\""
  4. echo $var
  5. echo "$var"
  6.  
  7. echo
  8.  
  9. IFS='\'  # Меняем разделитель полей
  10. echo $var
  11. echo "$var"

freebsd82 /work-tests/sh# ./test.sh
'(]\{}$"
'(]\{}$"

'(] {}$" # символ \ заменен пробелом
'(]\{}$"

Одиночные кавычки (' '), по своему действию схожи с двойными кавычками, но не разрешают внутри себя подстановку переменных, интерпретируя символ "$", как обычный символ. Любой символ внутри одинарных кавычек, за исключением символа одинарной кавычки, воспринимается как обычный символ.
Одинарные кавычки являются более строгим вариантом двойных кавычек.

Поскольку внутри одиночных кавычек даже экранирующий (\) символ воспринимается как обычный символ, попытка вывести одиночную кавычку внутри строки, ограниченной одинарными кавычками, не даст желаемого результата, хотя при желании это можно обойти.

  1. #!/usr/local/bin/bash
  2.  
  3. echo 'Why can't I write 's between single quotes'
  4. echo 'Why can'\''t I write '"'"'s between single quotes'

Вторая строка разбита на 3 подстроки, ограниченных одинарными кавычками, а между подстроками стоят экранированные одинарные кавычки, в первом случае кавычка экранируется с помощью обратного слэша, во втором, с помощью двойных кавычек.

freebsd82 /work-tests/sh# ./test.sh
Why cant I write s between single quotes
Why can't I write 's between single quotes

При использовании с некоторыми командами, например echo и sed, экранирующий символ может применяться для получения инверсного результата, когда обычные символы при экранировании приобретают специальное значение.

Специальное назначение экранированных символов в echo и sed

\n
Символ перевода строки (новая строка)
\r
Символ перевода каретки
\t
горизонтальная табуляция
\v
вертикальная табуляция
\b
забой (backspace)
\a
"звонок" (сигнал)
\0xx
ASCII-символ с кодом 0xx в восьмеричном виде)
\"
кавычки
echo "He said: \"Hi\"." # Будет выведено He said: "Hi".
\$
символ доллара. Если за конструкцией \$ идет переменная, подстановка значения этой переменной, выполнена не будет.
echo "\$var" # будет выведено $var
\\
обратный слэш
echo "\\" # выведет \

Поведение символа "\" ( обратный слэш ), зависит от разных факторов, таких как: экранирован-ли он, заключен-ли в кавычки, используется в подстановке команд или в конструкции "вложенный документ".

  1. #  Простое экранирование и кавычки
  2.                        # возвращаемое значение
  3. echo \z               #  z
  4. echo \\z              # \z
  5. echo '\z'             # \z
  6. echo '\\z'            # \\z
  7. echo "\z"             # \z
  8. echo "\\z"            # \z
  9.  
  10. #  Подстановка команды
  11. echo `echo \z`        #  z
  12. echo `echo \\z`       #  z
  13. echo `echo \\\z`      # \z
  14. echo `echo \\\\z`     # \z
  15. echo `echo \\\\\\z`   # \z
  16. echo `echo \\\\\\\z`  # \\z
  17. echo `echo "\z"`      # \z
  18. echo `echo "\\z"`     # \z
  19.  
  20. # Встроенный документ
  21. cat <<EOF
  22. \z
  23. EOF                   # \z
  24.  
  25. cat <<EOF
  26. \\z
  27. EOF                   # \z

Отдельные символы в строке, которая записывается в переменную, могут быть экранированы, исключение составляет сам экранирующий символ.

  1. #!/usr/local/bin/bash
  2.  
  3. var=\
  4. echo "$var"

Данный сценарий вернет сообщение об ошибке:

freebsd82 /sh# ./esc.sh
./esc: line 4: : command not found

Фактически, в данном примере, экранируется символ перевода строки, в итоге команда получается такой var=echo "$var".

  1. var=\
  2. test
  3. echo "$var"  # выведет test, поскольку вторая строка ничего не нарушает с точки зрения присвоения значений переменным
  4.  
  5. var=\  # после \ ставим пробел
  6. echo "$var" # вернет пробел
  7.  
  8. var=\\
  9. echo "$var"  # выведет \
  10.  
  11. var=\\\ # первый слэш экранирует второй, третий остается бесхозным
  12. echo "$var" # выведет сообщение об ошибке
  13.  
  14. var=\\\\ # первый слэш экранирует второй, третий экранирует четвертый
  15. echo "$var" # будет выведено \\

Экранирование пробелов в командной строке объединяет все аргументы в один, например команда:

freebsd82 /# ls /etc /root

вернет листинг каталогов /etc и /root, а вариант

freebsd82 /# ls /etc\ /root

вернет "No such file or directory", поскольку фактически мы сказали команде ls вывести листинг каталога /etc/root, которого не существует.

Очень часто символ экранирования \ ( обратный слэш ), используется для ввода из командной строки многострочных команд, экранируя символ новой строки:

freebsd82 /sh# ( cd ./dir1 && tar cf - .) | \
? ( cd ./dir2 && tar xpvf - )
x ./
x ./BOOBOO.TXT
x ./booboo.txt
x ./brrr

Приведенная команда копирует содержимое одной директории в другую, записана в 2 строки.
Другой вариант той-же операции:

freebsd82 /# tar cf - -C ./dir1 . |\
? tar xpvf - -C ./dir2
x ./
x ./BOOBOO.TXT
x ./booboo.txt
x ./brrr

Вообще если одна из строк многострочной команды заканчивается символом конвейера " | ", экранировать символ новой строки не обязательно, это просто считается хорошим тоном, и применимо только к оболочке bash, например в csh это не работает.

  1. echo "foo
  2. bar"

Выведет:
#foo
#bar

  1. echo 'foo
  2. bar'

С строгими кавычками, результат будет тот-же

  1. echo foo\
  2. bar

Перевод строки экранирован, поэтому вывод будет:
#foobar

  1. echo "foo\
  2. bar"    

Внутри не строгих кавычек слэш работает как экранирующий, вывод будет:
#foobar

  1. echo 'foo\
  2. bar'

В одинарных ( строгих ) кавычках, слэш интерпретируется как обычный символ и ничего не экранирует, вывод будет:
#foo\
#bar