Как избежать одинарных кавычек внутри строк, заключенных в одинарные кавычки

StackOverflow https://stackoverflow.com/questions/1250079

  •  12-09-2019
  •  | 
  •  

Вопрос

Допустим, у вас есть Bash alias Нравится:

alias rxvt='urxvt'

который прекрасно работает.

Однако:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

не сработает, и тоже не сработает:

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

Итак, как вы в конечном итоге сопоставляете открывающие и закрывающие кавычки внутри строки после того, как вы экранировали кавычки?

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

кажется неуклюжим, хотя это представляло бы одну и ту же строку, если бы вам было разрешено объединять их подобным образом.

Это было полезно?

Решение

Если вы действительно хотите использовать одинарные кавычки на самом внешнем слое, помните, что вы можете склеить оба типа кавычек.Пример:

 alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
 #                     ^^^^^       ^^^^^     ^^^^^       ^^^^
 #                     12345       12345     12345       1234

Объяснение того, как '"'"' интерпретируется как просто ':

  1. ' Завершить первую цитату, в которой используются одинарные кавычки.
  2. " Начните вторую цитату, используя двойные кавычки.
  3. ' Цитируемый персонаж.
  4. " Завершите вторую цитату, используя двойные кавычки.
  5. ' Начните третью цитату, используя одинарные кавычки.

Если вы не разместите пробелы между (1) и (2) или между (4) и (5), оболочка будет интерпретировать эту строку как одно длинное слово.

Другие советы

Я всегда просто заменяю каждую встроенную одинарную кавычку последовательностью: '\'' (то есть:quote обратная косая черта quote quote), которая закрывает строку, добавляет экранированную одинарную кавычку и снова открывает строку.


Я часто использую функцию «цитирование» в своих сценариях Perl, чтобы сделать это за меня.Шаги будут следующими:

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.

Это практически касается всех случаев.

Жизнь становится веселее, когда вы представляете eval в ваши сценарии оболочки.По сути, вам придется заново все цитировать!

Например, создайте Perl-скрипт под названием quotify, содержащий приведенные выше инструкции:

#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];

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

$ quotify
urxvt -fg '#111111' -bg '#111111'

результат:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

который затем можно скопировать/вставить в команду псевдонима:

alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

(Если вам нужно вставить команду в eval, запустите quotify еще раз:

 $ quotify
 alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

результат:

'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

который можно скопировать/вставить в eval:

eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

Поскольку Баш 2.04 синтаксис $'string' (вместо просто 'string';предупреждение:не путай с $('string')) — еще один механизм цитирования, который позволяет ANSI C-подобные escape-последовательности и выполните расширение до версии с одинарными кавычками.

Простой пример:

  $> echo $'aa\'bb'
  aa'bb

  $> alias myvar=$'aa\'bb'
  $> alias myvar
  alias myvar='aa'\''bb'

В твоем случае:

$> alias rxvt=$'urxvt -fg \'#111111\' -bg \'#111111\''
$> alias rxvt
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

Общие экранирующие последовательности работают как положено:

\'     single quote
\"     double quote
\\     backslash
\n     new line
\t     horizontal tab
\r     carriage return

Ниже приведена скопированная + вставленная соответствующая документация из man bash (версия 4.4):

Слова формы $'string' обрабатываются особым образом.Слово расширяется до строки с заменой символов с обратной косой чертой, как указано в стандарте ANSI C.Escape-последовательности обратной косой черты, если они присутствуют, декодируются следующим образом:

    \a     alert (bell)
    \b     backspace
    \e
    \E     an escape character
    \f     form feed
    \n     new line
    \r     carriage return
    \t     horizontal tab
    \v     vertical tab
    \\     backslash
    \'     single quote
    \"     double quote
    \?     question mark
    \nnn   the eight-bit character whose value is the octal 
           value nnn (one to three digits)
    \xHH   the eight-bit character whose value is the hexadecimal
           value HH (one or two hex digits)
    \uHHHH the Unicode (ISO/IEC 10646) character whose value is 
           the hexadecimal value HHHH (one to four hex digits)
    \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value 
               is the hexadecimal value HHHHHHHH (one to eight 
               hex digits)
    \cx    a control-x character

Расширенный результат заключается в одинарные кавычки, как если бы знак доллара отсутствовал.


Видеть Цитаты и побег:ANSI C-подобные строки на вики bash-hackers.org для получения более подробной информации.Также обратите внимание, что «Изменения Баша» файл (обзор здесь) много упоминает об изменениях и исправлениях ошибок, связанных с $'string' механизм цитирования.

По данным unix.stackexchange.com Как использовать специальный символ как обычный? он должен работать (с некоторыми изменениями) в bash, zsh, mksh, ksh93, FreeBSD и busybox sh.

Я не вижу записи в его блоге (ссылка, пожалуйста?) но, согласно справочное руководство gnu:

Заключение символов в одинарные кавычки (‘’) сохраняет буквальное значение каждого символа в кавычках. Одинарная кавычка может не встречаться между одинарными кавычками, даже если перед ними стоит обратная косая черта.

так что баш не поймет:

alias x='y \'z '

однако вы можете сделать это, если заключите в двойные кавычки:

alias x="echo \'y "
> x
> 'y

Я могу подтвердить это, используя '\'' одинарная кавычка внутри строки с одинарными кавычками работает в Bash, и ее можно объяснить так же, как аргумент «склеивания», приведенный ранее в теме.Предположим, у нас есть строка в кавычках: 'A '\''B'\'' C' (здесь все кавычки — одинарные).Если он передается в echo, он печатает следующее: A 'B' C.В каждом '\'' первая кавычка закрывает текущую строку в одинарных кавычках, следующая \' приклеивает одинарную кавычку к предыдущей строке (\' это способ указать одинарную кавычку без начала строки в кавычках), а последняя кавычка открывает другую строку в одинарных кавычках.

Простой пример экранирования кавычек в оболочке:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

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

Обе версии работают либо с конкатенацией, используя экранированную одинарную кавычку (\'), либо с конкатенацией, заключая символ одинарной кавычки в двойные кавычки ("'").

Автор вопроса не заметил, что в конце его последней попытки побега была лишняя одинарная кавычка ('):

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
           │         │┊┊|       │┊┊│     │┊┊│       │┊┊│
           └─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      └┴─────────┴┴───┰───┴┴─────────┴┘│
                          All escaped single quotes    │
                                                       │
                                                       ?

Как вы можете видеть в предыдущем красивом образце ASCII/Unicode, за последней экранированной одинарной кавычкой (\') следует ненужная одинарная кавычка (').Использование средства выделения синтаксиса, подобного тому, которое есть в Notepad++, может оказаться очень полезным.

То же самое справедливо и для другого примера, подобного следующему:

alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'

Эти два прекрасных примера псевдонимов очень сложным и запутанным образом показывают, как можно выстроить файл.То есть из файла с большим количеством строк вы получаете только одну строку с запятыми и пробелами между содержимым предыдущих строк.Чтобы понять смысл предыдущего комментария, приведем следующий пример:

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214

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

rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }

который вы затем можете назвать как:

rxvt 123456 654321

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

alias rxvt='rxvt 123456 654321'

или, если вам нужно включить # во всех вызовах почему-то:

rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }

который вы затем можете назвать как:

rxvt '#123456' '#654321'

тогда, конечно, псевдоним:

alias rxvt="rxvt '#123456' '#654321'"

(упс, кажется, я вроде как обратился к цитированию :)

Я просто использую шелл-коды..например \x27 или \\x22 применимо.Никаких хлопот, никогда.

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

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

В приведенном выше коде HEREDOC отправляется в cat команда, и ее выходные данные присваиваются переменной с помощью нотации подстановки команд. $(..)

Необходимо заключить HEREDOC в одинарную кавычку, поскольку он находится внутри $()

ИМХО, реальный ответ заключается в том, что вы не можете избежать одинарных кавычек внутри строк с одинарными кавычками.

Это невозможно.

Если предположить, что мы используем bash.

Из руководства по bash...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

Вам необходимо использовать один из других механизмов экранирования строк " или \

В этом нет ничего волшебного alias это требует использования одинарных кавычек.

Оба следующих варианта работают в bash.

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

Последний использует \ для экранирования пробела.

В #111111 также нет ничего волшебного, требующего одинарных кавычек.

Следующие параметры дают тот же результат, что и два других параметра, поскольку псевдоним rxvt работает должным образом.

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

Вы также можете избежать неприятного # напрямую

alias rxvt="urxvt -fg \#111111 -bg \#111111"

Большинство этих ответов касаются конкретного случая, о котором вы спрашиваете.Существует общий подход, который мы с другом разработали, который позволяет использовать произвольные кавычки в случае, если вам нужно цитировать команды bash через несколько уровней расширения оболочки, например, через ssh, su -c, bash -c, и т. д.Вам нужен один основной примитив, здесь, в родном bash:

quote_args() {
    local sq="'"
    local dq='"'
    local space=""
    local arg
    for arg; do
        echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
        space=" "
    done
}

Это делает именно то, что сказано:он цитирует каждый аргумент индивидуально (конечно, после расширения bash):

$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'

Для одного уровня расширения он делает очевидную вещь:

$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2

(Обратите внимание, что двойные кавычки вокруг $(quote_args ...) необходимы для того, чтобы превратить результат в один аргумент bash -c.) И в более широком смысле его можно использовать для правильного цитирования через несколько уровней раскрытия:

$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2

Приведенный выше пример:

  1. оболочка заключает каждый аргумент в кавычки во внутренний quote_args индивидуально, а затем объединяет полученный результат в один аргумент с внутренними двойными кавычками.
  2. кавычки оболочки bash, -c, и уже заключенный в кавычки результат шага 1, а затем объединяет результат в один аргумент с внешними двойными кавычками.
  3. отправляет этот беспорядок в качестве аргумента внешнему bash -c.

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

$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure

В первом примере bash сразу расширяет quote_args cd /; pwd 1>&2 на две отдельные команды, quote_args cd / и pwd 1>&2, так что CWD все еще /tmp когда pwd команда выполняется.Второй пример иллюстрирует аналогичную проблему для подстановки.Действительно, одна и та же основная проблема возникает со всеми расширениями Bash.Проблема здесь в том, что подстановка команды не является вызовом функции:он буквально оценивает один bash-скрипт и использует его результаты как часть другого bash-скрипта.

Если вы попытаетесь просто избежать операторов оболочки, у вас ничего не получится, поскольку результирующая строка будет передана в bash -c представляет собой просто последовательность строк в индивидуальных кавычках, которые затем не интерпретируются как операторы, что легко увидеть, если вы повторите строку, которая была бы передана в bash:

$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'

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

Следовательно, в самом общем смысле вам нужно заключить в кавычки каждое слово команды, не предназначенное для раскрытия во время подстановки команды, отдельно, и не применять никаких дополнительных кавычек к операторам оболочки:

$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success

Как только вы это сделаете, вся строка станет доступной для дальнейшего цитирования на произвольные уровни оценки:

$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success

и т. д.

Эти примеры могут показаться перегруженными, учитывая такие слова, как success, sbin, и pwd не нужно заключать в кавычки, но ключевой момент, который следует помнить при написании сценария, принимающего произвольный ввод, заключается в том, что вы хотите заключить в кавычки все, в чем вы не совсем уверены. не делает нужно цитировать, потому что никогда не знаешь, когда пользователь вставит Robert'; rm -rf /.

Чтобы лучше понять, что происходит под обложкой, вы можете поиграть с двумя небольшими вспомогательными функциями:

debug_args() {
    for (( I=1; $I <= $#; I++ )); do
        echo -n "$I:<${!I}> " 1>&2
    done
    echo 1>&2
}

debug_args_and_run() {
    debug_args "$@"
    "$@"
}

который будет перечислять каждый аргумент команды перед ее выполнением:

$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

В данном примере в качестве внешнего механизма escape просто используются двойные кавычки вместо одинарных:

alias rxvt="urxvt -fg '#111111' -bg '#111111'"

Этот подход подходит для многих случаев, когда вы просто хотите передать команде фиксированную строку:Просто проверьте, как оболочка будет интерпретировать строку в двойных кавычках через echo, и при необходимости экранируйте символы обратной косой чертой.

В этом примере вы увидите, что двойных кавычек достаточно для защиты строки:

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'

Вот разработка «Единственного истинного ответа», упомянутого выше:

Иногда я загружаю с помощью rsync через ssh, и мне приходится экранировать имя файла ДВАЖДЫ!(Боже мой!) Один раз для bash и один раз для ssh.Здесь действует тот же принцип чередования разделителей кавычек.

Например, предположим, что мы хотим получить:Лос-Анджелесские истории Луи Теру...

  1. Сначала вы заключаете имя Луи Теру в одинарные кавычки для bash и двойные для ssh:«Луи Теру»
  2. Затем вы используете одинарные кавычки, чтобы избежать двойной кавычки '"'
  3. Используйте двойные кавычки, чтобы избежать апострофа "'"
  4. Затем повторите № 2, используя одинарные кавычки, чтобы избежать двойной кавычки '"'.
  5. Затем заключите LA Stories в одинарные кавычки для bash и двойные кавычки для ssh:«Лос-Анджелесские истории»

И вот!В итоге вы получите следующее:

rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'

а это очень много работы для одного маленького ' -- но вот и все

Еще один способ решить проблему слишком большого количества слоев вложенных цитат:

Вы пытаетесь втиснуть слишком много в слишком маленькое пространство, поэтому используйте функцию bash.

Проблема в том, что вы пытаетесь создать слишком много уровней вложенности, а базовая технология псевдонимов недостаточно мощна, чтобы ее можно было реализовать.Используйте подобную функцию bash, чтобы одинарные и двойные кавычки, а также передаваемые параметры обрабатывались нормально, как и следовало ожидать:

lets_do_some_stuff() {
    tmp=$1                       #keep a passed in parameter.
    run_your_program $@          #use all your passed parameters.
    echo -e '\n-------------'    #use your single quotes.
    echo `date`                  #use your back ticks.
    echo -e "\n-------------"    #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff

Затем вы можете использовать переменные $1 и $2, а также одинарные, двойные кавычки и обратные тики, не беспокоясь о том, что функция псевдонима нарушит их целостность.

Эта программа печатает:

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

Объяснение реализации:

  • двойные кавычки, чтобы мы могли легко выводить одинарные кавычки-обертки и использовать ${...} синтаксис

  • Поиск и замена bash выглядит так: ${varname//search/replacement}

  • мы заменяем ' с '\''

  • '\'' кодирует один ' вот так:

    1. ' завершает одинарное цитирование

    2. \' кодирует ' (обратная косая черта необходима, потому что мы не в кавычках)

    3. ' снова запускает одинарные кавычки

    4. bash автоматически объединяет строки без пробелов между ними.

  • есть \ перед каждым \ и ' потому что это правила экранирования для ${...//.../...} .

string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded:  $(shell_escape "$string")"
echo "expanded: $(bash -c "echo $(shell_escape "$string")")"

P.S.Всегда кодируйте строки в одинарных кавычках, потому что они намного проще, чем строки в двойных кавычках.

Если у вас установлен GNU Parallel, вы можете использовать его внутреннее цитирование:

$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record

Начиная с версии 20190222 вы даже можете --shellquote много раз:

$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record

Он будет цитировать строку во всех поддерживаемых оболочках (не только bash).

Эта функция:

quote () 
{ 
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

позволяет цитировать ' внутри '.Используйте как это:

$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

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

#!/bin/bash

quote ()
{
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

while read line; do
    quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

Выведет:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'

Все правильно заключенные строки в одинарные кавычки.

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

$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'

Объяснение

Ключевым моментом является то, что вы можете закрыть одинарную кавычку и открыть ее столько раз, сколько захотите.Например foo='a''b' такой же как foo='ab'.Таким образом, вы можете закрыть одинарную кавычку, добавить буквальную одинарную кавычку. \', затем снова откройте следующую одинарную кавычку.

Диаграмма разбивки

На этой диаграмме это ясно показано с помощью скобок, показывающих, где одинарные кавычки открываются и закрываются.Кавычки не являются «вложенными», как круглые скобки.Также можно обратить внимание на цветовое мелирование, которое правильно нанесено.Строки в кавычках окрашены в темно-бордовый цвет, тогда как \' черный.

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'    # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^    # show open/close quotes
 urxvt -fg   ' #111111  '  -bg   ' #111111  '    # literal characters remaining

(По сути, это тот же ответ, что и у Адриана, но мне кажется, что это лучше объясняет.Также в его ответе есть две лишние одинарные кавычки в конце.)

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

single_quote() {
  local quoted="'"
  local i=0
  while [ $i -lt ${#1} ]; do
    local ch="${1:i:1}"
    if [[ "$ch" != "'" ]]; then
      quoted="$quoted$ch"
    else
      local single_quotes="'"
      local j=1
      while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
        single_quotes="$single_quotes'"
        ((j++))
      done
      quoted="$quoted'\"$single_quotes\"'"
      ((i+=j-1))
    fi
    ((i++))
  done
  echo "$quoted'"
}

Итак, вы можете использовать его следующим образом:

single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'

Если вы генерируете строку оболочки в Python 2 или Python 3, следующее может помочь в цитировании аргументов:

#!/usr/bin/env python

from __future__ import print_function

try:  # py3
    from shlex import quote as shlex_quote
except ImportError:  # py2
    from pipes import quote as shlex_quote

s = """foo ain't "bad" so there!"""

print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))

Это выведет:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top