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

Допустим, у вас есть 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'\''

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

Ответ 1

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

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

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

  • ' Завершить первую цитату, которая использует одинарные кавычки.
  • " Запустите вторую цитату, используя двойные кавычки.
  • ' Цитируемый символ.
  • " Завершить вторую цитату, используя двойные кавычки.
  • ' Запустите третью цитату, используя одинарные кавычки.

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

Ответ 2

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


Я часто выставляю функцию "quotify" в своих сценариях Perl, чтобы сделать это для меня. Этапы:

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

Это в значительной степени касается всех случаев.

Жизнь становится более увлекательной, когда вы вводите eval в свои shell-скрипты. Вам, по сути, нужно снова повторить все!

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

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

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

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

результат:

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

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

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

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

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

результат:

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

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

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

Ответ 3

Поскольку синтаксис Bash 2.04 $'string' (вместо просто 'string'; предупреждение: не путайте с $('string')) - это еще один механизм цитирования, который позволяет ANSI C-like 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. Последующие последовательности обратной косой черты, если они имеются, декодируются следующим образом:

    \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 wiki для более подробной информации. Также обратите внимание, что в файле "Bash Changes" (обзор здесь) много говорится об изменениях и исправлениях ошибок, связанных с механизмом цитирования $'string'.

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

Ответ 4

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

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

поэтому bash не поймет:

alias x='y \'z '

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

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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'"

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

Ответ 9

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

Ответ 10

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

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

alias rxvt=$command

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

Ввод одной цитаты вокруг HEREDOC необходим, поскольку он находится в пределах $()

Ответ 11

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

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

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

Из bash manual...

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.

Вам нужно использовать один из других механизмов escape-последовательности строки "или\

В 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"

Ответ 12

Большинство этих ответов затрагивают конкретный случай, о котором вы спрашиваете. Существует общий подход, разработанный мной и мной, который позволяет произвольно цитировать, если вам нужно процитировать команды bash через несколько слоев расширения оболочки, например, через ssh, su -c, bash -c и т.д. это один основной примитив, который вам нужен, здесь в native 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

В приведенном выше примере:

  • shell ссылается на каждый аргумент на внутренний quote_args отдельно и затем объединяет полученный результат в один аргумент с внутренними двойными кавычками.
  • shell-кавычки bash, -c и уже один раз цитируемый результат с шага 1, а затем объединяет результат в один аргумент с внешними двойными кавычками.
  • отправляет этот беспорядок в качестве аргумента во внешний 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. Второй пример иллюстрирует аналогичную проблему для globbing. Действительно, та же основная проблема возникает при всех разложениях bash. Проблема заключается в том, что подстановка команды не является вызовом функции: она буквально оценивает один bash script и использует его вывод как часть другого bash script.

Если вы попытаетесь просто выйти из операторов оболочки, вы потерпите неудачу, потому что результирующая строка, переданная в 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 не обязательно должны быть закодированы в кавычки, но ключевой момент, который следует помнить при записи script с произвольного ввода, - это что вы хотите процитировать все, что вы не совсем уверены, не нужно цитировать, потому что вы никогда не знаете, когда пользователь будет вбрасывать 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 "[email protected]"
    "[email protected]"
}

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

$ 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

Ответ 13

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

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

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

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

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

Ответ 14

Вот выдержка из "One True Answer", упомянутая выше:

Иногда я буду загружать с помощью rsync поверх ssh и выходить из файла с именем 'it it TWICE! (OMG!) Однажды для bash и один раз для ssh. Здесь действует тот же принцип чередования разделителей цитат.

Например, скажем, мы хотим получить: Louis Theroux LA Stories...

  • Сначала вы включаете Louis Theroux в одинарные кавычки для bash и двойные кавычки для ssh: ' "Луи Theroux" '
  • Затем вы используете одинарные кавычки, чтобы избежать двойной кавычки '' '
  • Использование двойных кавычек для выхода из апострофа ""
  • Затем повторите # 2, используя одинарные кавычки, чтобы избежать двойной кавычки '' '
  • Затем включите LA Stories в одинарные кавычки для bash и двойные кавычки для ssh: ' "LA Stories" '

И вот! Вы завершаете это:

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

который очень много работает для одного маленького ", но там вы идете

Ответ 15

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

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

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

lets_do_some_stuff() {
    tmp=$1                       #keep a passed in parameter.
    run_your_program [email protected]          #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 и одиночные, двойные кавычки и обратные тики, не беспокоясь о том, что функция псевдонима разрушает их целостность.

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

[email protected] ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------

Ответ 16

shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

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

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

  • bash search and replace выглядит так: ${varname//search/replacement}

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

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

    1. ' заканчивается одиночное цитирование

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

    3. ' Начинает сингл снова процитировать

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

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

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

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

Ответ 17

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

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

С версии 20190222 вы можете даже --shellquote несколько раз:

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

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

Ответ 18

Эта функция:

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

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

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

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

#!/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 LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

Будет выводиться:

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

Все правильно цитируемые строки внутри одинарных кавычек.

Ответ 19

Очевидно, было бы проще просто окружить двойными кавычками, но где вызов в этом? Вот ответ, используя только одинарные кавычки. Я использую переменную вместо 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

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

Ответ 20

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

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'

Ответ 21

Если вы генерируете строку оболочки в 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!'