Какие символы нужно избегать при использовании Bash?

Есть ли исчерпывающий список символов, которые нужно экранировать в Bash? Можно ли его проверить только с помощью sed?

В частности, я проверял, нужно ли экранировать % или нет. Я попробовал

echo "h%h" | sed 's/%/i/g'

и работал нормально, без выхода %. Означает ли это, что % не нужно бежать? Было ли это хорошим способом проверить необходимость?

И более общие: являются ли они одинаковыми символами для выхода в shell и bash?

Ответ 1

Существует два простых и безопасных правила, которые работают не только в sh, но и bash.

1. Поместите всю строку в одинарные кавычки

Это работает для всех символов, кроме одиночной кавычки. Чтобы избежать одиночной кавычки, закройте перед этим цитату, вставьте одну кавычку и снова откройте цитату.

'I'\''m a [email protected] $tring which ends in newline
'

Команда sed: sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2. Побег каждого char с обратным слэшем

Это работает для всех символов, кроме новой строки. Для символов новой строки используются одиночные или двойные кавычки. Пустые строки должны быть обработаны - замените на ""

\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"

команда sed: sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.

2b. Более читаемая версия 2

Здесь есть простой безопасный набор символов, например [a-zA-Z0-9,._+:@%/-], который может быть оставлен без сохранения, чтобы он был более читабельным

I\'m\ a\ [email protected]\ \$tring\ which\ ends\ in\ newline"
"

команда sed: LC_ALL=C sed -e 's/[^a-zA-Z0-9,[email protected]%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.


Обратите внимание, что в sed-программе невозможно узнать, заканчивается ли последняя строка ввода байт новой строки (кроме случаев, когда она пуста). Вот почему оба над командами sed предполагают, что это не так. Вы можете добавить цитированную новую строку вручную.

Обратите внимание, что переменные оболочки определены только для текста в смысле POSIX. Обработка двоичных данных не определена. Для реализаций, которые имеют значение, бинарные работы, за исключением NUL байтов (поскольку переменные реализованы с помощью строк C и предназначены для использования как строки C, а именно аргументы программы), но вы должны переключиться на "двоичную" локаль, такую ​​как latin1.


(Вы можете легко проверить правила, прочитав спецификацию POSIX для sh. Для bash проверьте справочное руководство, связанное с @AustinPhillips)

Ответ 2

Чтобы спасти кого-то еще от RTFM... в bash:

Закрывающие символы в двойных кавычках сохраняют литеральное значение всех символов в кавычках, за исключением $, `, \, и, когда расширение истории включено, !.

... так что, если вы избежите этих (и самих цитат, конечно), вы, вероятно, все в порядке.

Если вы придерживаетесь более консервативного подхода "когда сомневаетесь, избегайте его", должно быть возможно избегать использования символов с особым значением, не избегая символов-идентификаторов (например, букв ASCII, цифр или "_" ). Очень маловероятно, что они когда-либо (то есть в какой-то странной оболочке POSIX-ish) имеют особое значение и, следовательно, должны быть экранированы.

Ответ 3

формат, который может быть повторно использован как ввод оболочки

Для этого типа запроса существует специальная директива формата printf (%q):

printf [-v var] формат [аргументы]

 %q     causes printf to output the corresponding argument
        in a format that can be reused as shell input.

Некоторые образцы:

read foo
Hello world
printf "%q\n" "$foo"
Hello\ world

printf "%q\n" $'Hello world!\n'
$'Hello world!\n'

Это также можно использовать с помощью переменных:

printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'

Быстрая проверка со всеми (128) байтами ascii:

Обратите внимание, что все байты от 128 до 255 должны быть экранированы.

for i in {0..127} ;do
    printf -v var \\%o $i
    printf -v var $var
    printf -v res "%q" "$var"
    esc=E
    [ "$var" = "$res" ] && esc=-
    printf "%02X %s %-7s\n" $i $esc "$res"
done |
    column

Это должно выглядеть примерно так:

00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
12 E $'\022'    2C E \,         46 - F          60 E \'         7A - z      
13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
18 E $'\030'    32 - 2          4C - L          66 - f      
19 E $'\031'    33 - 3          4D - M          67 - g      

Где первое поле является шестнадцатеричным значением байта, второе содержит E если символ должен быть экранирован, а третье поле - экранированное представление символа.

Почему ,?

Вы можете увидеть некоторые символы, которые не всегда должны быть экранированы, как ,, } и {.

Поэтому не всегда, а когда-то:

echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.

или

echo test { 1, 2, 3 }
test { 1, 2, 3 }

но уход:

echo test{1,2,3}
test1 test2 test3

echo test\ {1,2,3}
test 1 test 2 test 3

echo test\ {\ 1,\ 2,\ 3\ }
test  1 test  2 test  3

echo test\ {\ 1\,\ 2,\ 3\ }
test  1, 2 test  3 

Ответ 4

Символы, которые нуждаются в экранировании, различаются в оболочке Bourne или POSIX, чем Bash. Обычно (очень) Bash является надмножеством этих оболочек, поэтому все, что вы избегаете в shell, должно быть экранировано в Bash.

Хорошим общим правилом было бы "если в сомнении, убежать". Но ускользание некоторых символов дает им особое значение, например \n. Они перечислены на страницах man bash в разделе Quoting и echo.

Кроме этого, избегайте любого символа, который не является буквенно-цифровым, это безопаснее. Я не знаю ни одного окончательного списка.

Страницы руководства перечисляют их все где-то, но не в одном месте. Изучите язык, это способ убедиться.

Тот, кто меня поймал, !. Это особый символ (расширение истории) в Bash (и csh), но не в оболочке Korn. Даже echo "Hello world!" дает проблемы. Использование одиночных кавычек, как обычно, устраняет особый смысл.

Ответ 5

Используя технику print '%q', мы можем запустить цикл, чтобы узнать, какие символы являются специальными:

#!/bin/bash
special=$''[email protected]#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
    char="${special:i:1}"
    printf -v q_char '%q' "$char"
    if [[ "$char" != "$q_char" ]]; then
        printf 'Yes - character %s needs to be escaped\n' "$char"
    else
        printf 'No - character %s does not need to be escaped\n' "$char"
    fi
done | sort

Он дает этот результат:

No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character   needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ' needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped

Некоторые из результатов, например , выглядят немного подозрительно. Было бы интересно получить данные @CharlesDuffy.

Ответ 6

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

Лучшей ссылкой является Quoting раздел руководства bash.

В нем объясняется, какие символы нужно экранировать. Обратите внимание, что некоторым символам может потребоваться экранирование в зависимости от того, какие опции включены, например, расширение истории.

Ответ 7

Я заметил, что bash автоматически удаляет некоторые символы при использовании автозаполнения.

Например, если у вас есть каталог с именем dir:A, bash будет автоматически завершен до dir\:A

Используя это, я провел несколько экспериментов с использованием символов таблицы ASCII и получил следующие списки:

Символы, которые bash выполняет при автозавершении: (включает пробел)

 !"$&'()*,:;<=>[email protected][\]^`{|}

Символы, которые bash не исчезают:

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

(я исключил /, поскольку он не может использоваться в именах каталогов)