Какие символы необходимо экранировать при использовании Bash?

116

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

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

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

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

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

  • 4
    В общем, если вы заботитесь, вы делаете это неправильно. Обработка данных никогда не должна включать в себя прохождение через процесс синтаксического анализа и оценки, используемый для кода, что позволяет избежать спора. Это очень близко к лучшим практикам для SQL - где правильно использовать переменные связывания, а неправильное - пытаться «дезинфицировать» данные, введенные с помощью подстановок строк.
  • 0
    Связано с stackoverflow.com/questions/2854655/…
Показать ещё 9 комментариев
Теги:
special-characters
escaping

7 ответов

151
Лучший ответ

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

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

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

'I'\''m a s@fe $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\ 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)

  • 0
    Примечание: хороший вариант № 1 можно увидеть здесь: github.com/scop/bash-completion/blob/… . Не требует запуска sed , но требует bash .
  • 3
    Примечание для любого другого (как я!), Который изо всех сил пытается заставить их работать .... похоже, что вкус sed, который вы получаете в OSX, не запускает эти команды sed должным образом. Они прекрасно работают на Linux, хотя!
Показать ещё 9 комментариев
24

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

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

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

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

  • 1
    вот руководство, приведенное выше: gnu.org/software/bash/manual/html_node/Double-Quotes.html
  • 0
    Это короткий, приятный и в основном правильный ответ (+1 за это), но, возможно, еще лучше использовать одинарные кавычки - см. Мой более длинный ответ.
23

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

Для этого типа запроса существует специальная директива формата 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 
  • 0
    Это проблема в том, что, вызывая pritnf через bash / sh, строка должна быть сначала экранирована для bash / sh
  • 1
    @ThorSummoner, если вы не передадите строку как буквальный аргумент в оболочку из другого языка (где вы, вероятно, уже знаете, как заключать в кавычки). В Python: subprocess.Popen(['bash', '-c', 'printf "%q\0" "$@"', '_', arbitrary_string], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate() предоставит вам должным образом заключенную в кавычки версию arbitrary_string .
Показать ещё 1 комментарий
17

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

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

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

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

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

  • 1
    Мне особенно нравится хорошее общее правило: совет «если сомневаешься, избегай его» . Тем не менее есть сомнения в том, что проверка с помощью sed достаточно хороша, чтобы увидеть, нужно ли ее избегать. Спасибо за Ваш ответ!
  • 2
    @fedorqui: проверка с помощью sed не обязательна, вы можете проверить практически что угодно. sed не проблема, bash есть. Внутри одинарных кавычек нет специальных символов (кроме одинарных), вы даже не можете экранировать символы. Команда sed обычно должна быть в одинарных кавычках, потому что метасимволы RE имеют слишком много перекрытий с метасимволами оболочки, чтобы быть безопасными. Исключением является встраивание переменных оболочки, что должно быть сделано осторожно.
Показать ещё 1 комментарий
15

Используя технику 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.

  • 1
    Вы можете прочитать ответ , посмотрите немного подозрительно на последний абзац моего ответа
3

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

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

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

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

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

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

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

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

  • 2
    Если вы действительно хотите получить исчерпывающий список, я бы посоветовал посмотреть, какие символы printf %q изменяет и не изменяет, если передается в качестве аргумента - в идеале, проходя весь набор символов.
  • 0
    Бывают случаи, когда даже со строкой апострофа вы можете экранировать буквы и цифры, чтобы получить специальные символы. Например: tr '\ n' '\ t', который переводит символы новой строки в символы табуляции.
3

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

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

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

  • 3
    Таким образом, это подтверждает, что побег это такие джунгли без простого решения, придется проверять каждый случай. Спасибо!
  • 0
    @fedorqui Как и на любом языке, существует ряд правил, которым нужно следовать. Для экранирования строки bash набор правил довольно мал, как описано в руководстве. Самая простая строка - это одинарные кавычки, так как ничего не нужно экранировать. Тем не менее, нет способа включить одну кавычку в одну строку в кавычках.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню