Есть ли исчерпывающий список символов, которые нужно экранировать в Bash? Можно ли его проверить только с помощью sed
?
В частности, я проверял, нужно ли экранировать %
или нет. Я попробовал
echo "h%h" | sed 's/%/i/g'
и работал нормально, без выхода %
. Означает ли это, что %
не нужно бежать? Было ли это хорошим способом проверить необходимость?
И более общие: являются ли они одинаковыми символами для выхода в shell
и bash
?
Существует два простых и безопасных правила, которые работают не только в sh
, но и bash
.
Это работает для всех символов, кроме одиночной кавычки. Чтобы избежать одиночной кавычки, закройте перед этим цитату, вставьте одну кавычку и снова откройте цитату.
'I'\''m a s@fe $tring which ends in newline
'
Команда sed: sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"
Это работает для всех символов, кроме новой строки. Для символов новой строки используются одиночные или двойные кавычки. Пустые строки должны быть обработаны - замените на ""
\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/$/"/'
.
Здесь есть простой безопасный набор символов, например [a-zA-Z0-9,._+:@%/-]
, который может быть оставлен без сохранения, чтобы он был более читабельным
I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"
команда sed: LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'
.
Обратите внимание, что в sed-программе невозможно узнать, заканчивается ли последняя строка ввода байт новой строки (кроме случаев, когда она пуста). Вот почему оба над командами sed предполагают, что это не так. Вы можете добавить цитированную новую строку вручную.
Обратите внимание, что переменные оболочки определены только для текста в смысле POSIX. Обработка двоичных данных не определена. Для реализаций, которые имеют значение, бинарные работы, за исключением NUL байтов (поскольку переменные реализованы с помощью строк C и предназначены для использования как строки C, а именно аргументы программы), но вы должны переключиться на "двоичную" локаль, такую как latin1.
(Вы можете легко проверить правила, прочитав спецификацию POSIX для sh
. Для bash проверьте справочное руководство, связанное с @AustinPhillips)
sed
, но требует bash
.
Чтобы спасти кого-то еще от RTFM... в bash:
Закрывающие символы в двойных кавычках сохраняют литеральное значение всех символов в кавычках, за исключением
$
,`
,\
, и, когда расширение истории включено,!
.
... так что, если вы избежите этих (и самих цитат, конечно), вы, вероятно, все в порядке.
Если вы придерживаетесь более консервативного подхода "когда сомневаетесь, избегайте его", должно быть возможно избегать использования символов с особым значением, не избегая символов-идентификаторов (например, букв ASCII, цифр или "_" ). Очень маловероятно, что они когда-либо (то есть в какой-то странной оболочке POSIX-ish) имеют особое значение и, следовательно, должны быть экранированы.
Для этого типа запроса существует специальная директива формата 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 до 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
subprocess.Popen(['bash', '-c', 'printf "%q\0" "$@"', '_', arbitrary_string], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
предоставит вам должным образом заключенную в кавычки версию arbitrary_string
.
Символы, которые нуждаются в экранировании, различаются в оболочке Bourne или POSIX, чем Bash. Обычно (очень) Bash является надмножеством этих оболочек, поэтому все, что вы избегаете в shell
, должно быть экранировано в Bash.
Хорошим общим правилом было бы "если в сомнении, убежать". Но ускользание некоторых символов дает им особое значение, например \n
. Они перечислены на страницах man bash
в разделе Quoting
и echo
.
Кроме этого, избегайте любого символа, который не является буквенно-цифровым, это безопаснее. Я не знаю ни одного окончательного списка.
Страницы руководства перечисляют их все где-то, но не в одном месте. Изучите язык, это способ убедиться.
Тот, кто меня поймал, !
. Это особый символ (расширение истории) в Bash (и csh), но не в оболочке Korn. Даже echo "Hello world!"
дает проблемы. Использование одиночных кавычек, как обычно, устраняет особый смысл.
sed
достаточно хороша, чтобы увидеть, нужно ли ее избегать. Спасибо за Ваш ответ!
sed
не обязательна, вы можете проверить практически что угодно. sed
не проблема, bash
есть. Внутри одинарных кавычек нет специальных символов (кроме одинарных), вы даже не можете экранировать символы. Команда sed
обычно должна быть в одинарных кавычках, потому что метасимволы RE имеют слишком много перекрытий с метасимволами оболочки, чтобы быть безопасными. Исключением является встраивание переменных оболочки, что должно быть сделано осторожно.
Используя технику print '%q'
, мы можем запустить цикл, чтобы узнать, какие символы являются специальными:
#!/bin/bash
special=$''!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
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.
,
посмотрите немного подозрительно на последний абзац моего ответа
Я заметил, что bash автоматически удаляет некоторые символы при использовании автозаполнения.
Например, если у вас есть каталог с именем dir:A
, bash будет автоматически завершен до dir\:A
Используя это, я провел несколько экспериментов с использованием символов таблицы ASCII и получил следующие списки:
Символы, которые bash выполняет при автозавершении: (включает пробел)
!"$&'()*,:;<=>?@[\]^`{|}
Символы, которые bash не исчезают:
#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~
(я исключил /
, поскольку он не может использоваться в именах каталогов)
printf %q
изменяет и не изменяет, если передается в качестве аргумента - в идеале, проходя весь набор символов.
Я предполагаю, что вы говорите о bash строках. Существуют различные типы строк, которые имеют различный набор требований к экранированию. например. Строки одиночных кавычек отличаются от строк с двойными кавычками.
Лучшей ссылкой является Quoting раздел руководства bash.
В нем объясняется, какие символы нужно экранировать. Обратите внимание, что некоторым символам может потребоваться экранирование в зависимости от того, какие опции включены, например, расширение истории.