Как я могу избежать произвольной строки для использования в качестве аргумента командной строки в Bash?
У меня есть список строк, и я хочу передать эти строки в качестве аргументов в одном вызове командной строки Bash. Для простых буквенно-цифровых строк достаточно просто передать их дословно:
> script.pl foo bar baz yes no
foo
bar
baz
yes
no
Я понимаю, что если аргумент содержит пробелы или обратную косую черту или двойные кавычки, мне нужно обратная косая черта - избегайте двойных кавычек и обратных косых черт, а затем дважды указывайте аргумент.
> script.pl foo bar baz "\"yes\"\\\"no\""
foo
bar
baz
"yes"\"no"
Но когда аргумент содержит восклицательный знак, это происходит:
> script.pl !foo
-bash: !foo: event not found
Двойное цитирование не работает:
> script.pl "!foo"
-bash: !foo: event not found
Не работает обратная косая черта (обратите внимание, как буквальный обратный слэш присутствует на выходе):
> script.pl "\!foo"
\!foo
Я мало знаю о Bash, но я знаю, что есть другие специальные персонажи, которые делают подобные вещи. Какова общая процедура безопасного экранирования произвольной строки для использования в качестве аргумента командной строки в Bash?. Предположим, что строка может быть произвольной длины и содержать произвольные комбинации специальных символов. Я хотел бы использовать подпрограмму escape()
, которую я могу использовать, как показано ниже (пример Perl):
$cmd = join " ", map { escape($_); } @args;
Вот несколько примеров строк, которые должны быть безопасно экранированы этой функцией (я знаю, что некоторые из них выглядят как Windows, например, преднамеренно):
yes
no
Hello, world [string with a comma and space in it]
C:\Program Files\ [path with backslashes and a space in it]
" [i.e. a double-quote]
\ [backslash]
\\ [two backslashes]
\\\ [three backslashes]
\\\\ [four backslashes]
\\\\\ [five backslashes]
"\ [double-quote, backslash]
"\T [double-quote, backslash, T]
"\\T [double-quote, backslash, backslash, T]
!1
!A
"!\/'" [double-quote, exclamation, backslash, forward slash, apostrophe, double quote]
"Jeff's!" [double-quote, J, e, f, f, apostrophe, s, exclamation, double quote]
$PATH
%PATH%
&
<>|&^
*@[email protected]#?-_
EDIT:
Будет ли это трюк? Побег каждого необычного персонажа с обратной косой чертой и опускание одиночных или двойных кавычек. (Пример в Perl, но любой язык может это сделать)
sub escape {
$_[0] =~ s/([^a-zA-Z0-9_])/\\$1/g;
return $_[0];
}
Ответы
Ответ 1
Если вы хотите безошибочно указать что-либо для Bash, вы можете использовать его встроенное форматирование printf %q
:
cat strings.txt
:
yes
no
Hello, world
C:\Program Files\
"
\
\\
\\\
\\\\
\\\\\
"\
"\T
"\\T
!1
!A
"!\/'"
"Jeff's!"
$PATH
%PATH%
&
<>|&^
*@[email protected]#?-_
cat quote.sh
:
#!/bin/bash
while IFS= read -r -d $'\n'
do
printf %q "$REPLY"
printf '\n'
done < strings.txt
./quote.sh
:
yes
no
Hello\,\ world
C:\\Program\ Files\\
\"
\\
\\\\
\\\\\\
\\\\\\\\
\\\\\\\\\\
\"\\
\"\\T
\"\\\\T
\!1
\!A
\"\!\\/\'\"
\"Jeff\'s\!\"
\$PATH
%PATH%
\&
\<\>\|\&\^
\*@\$\$A\[email protected]#\?-_
Эти строки могут быть скопированы дословно, например, echo
для вывода исходных строк в файле strings.txt.
Ответ 2
Какова общая процедура безопасного экранирования произвольной строки для использования в качестве аргумента командной строки в Bash?
Замените каждое вхождение '
на '\''
, затем поместите '
в начало и конец.
Каждый символ, за исключением одной кавычки, может использоваться дословно в строке с разделителями с одной кавычкой. Невозможно поместить одну цитату внутри строки с одной кавычкой, но достаточно легко работать: завершите строку ('
), затем добавьте одну цитату, используя обратную косую черту, чтобы ее избежать (\'
), затем начните новую строку ('
).
Насколько я знаю, это всегда будет работать без исключений.
Ответ 3
Вы можете использовать одинарные кавычки для вывода строк для Bash. Обратите внимание, однако это не расширяет переменные в кавычках, как это делают двойные кавычки. В вашем примере должно работать следующее:
script.pl '!foo'
Из Perl это зависит от функции, которую вы используете, чтобы вызвать внешний процесс. Например, если вы используете функцию system
, вы можете передавать аргументы как параметры, поэтому нет необходимости их избегать. "d все равно нужно избегать цитат для Perl:
system("/usr/bin/rm", "-fr", "/tmp/CGI_test", "/var/tmp/CGI");
Ответ 4
sub text_to_shell_lit(_) {
return $_[0] if $_[0] =~ /^[a-zA-Z0-9_\-]+\z/;
my $s = $_[0];
$s =~ s/'/'\\''/g;
return "'$s'";
}
См. предыдущий post для примера.
Ответ 5
Всякий раз, когда вы видите, что вы не получаете желаемый результат, используйте следующий метод:
"""\special character"""
где специальный символ может включать ! " * ^ % $ # @
....
Например, если вы хотите создать bash генерирующий другой файл bash, в котором есть строка, и вы хотите присвоить ей значение, вы можете иметь следующий пример сценария:
Area="(1250,600),(1400,750)"
printf "SubArea="""\""""${Area}"""\""""\n" > test.sh
printf "echo """\$"""{SubArea}" >> test.sh
Тогда файл test.sh
будет иметь следующий код:
SubArea="(1250,600),(1400,750)"
echo ${SubArea}
В качестве напоминания о новой строке \n
следует использовать printf
.
Ответ 6
Bash интерпретирует восклицательные знаки только в интерактивном режиме.
Вы можете предотвратить это, выполнив:
set +o histexpand
Внутри двойных кавычек вы должны избегать знаков доллара, двойных кавычек, обратных косых черт, и я бы сказал, что все.
Ответ 7
Это не полный ответ, но иногда мне полезно использовать два типа цитат для одной строки путем их конкатенации, например echo "$HOME"'/foo!?.*'
.