Jak uniknąć pojedynczych cytatów w pojedynczych cytowanych ciągach?

Powiedzmy, że masz bash alias Jak:

alias rxvt='urxvt'
Co działa dobrze.

Jednakże:

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

Nie zadziała, ani nie zadziała:

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

Więc jak kończy się dopasowanie otwieranie i zamykanie cudzysłowów wewnątrz ciągu po uniknięciu cudzysłowów?

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

Wydaje się bezbożny, chociaż reprezentowałby ten sam ciąg znaków, jeśli możesz je tak łączyć.

Author: codeforester, 2009-08-09

19 answers

Jeśli naprawdę chcesz używać pojedynczych cudzysłowów w zewnętrznej warstwie, pamiętaj, że możesz przyklejać oba rodzaje cudzysłowów. Przykład:

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

Wyjaśnienie jak '"'"' jest interpretowane jako tylko ':

  1. ' Zakończ pierwszy cytat, który używa pojedynczych cudzysłowów.
  2. " Uruchom drugi cudzysłów, używając cudzysłowów podwójnych.
  3. ' cytowany znak.
  4. " Zakończ drugi cudzysłów, używając cudzysłowów podwójnych.
  5. ' Rozpocznij trzeci notowanie, używając pojedynczego cytaty.

Jeśli nie umieścisz żadnych spacji między (1) i (2) lub między (4) i (5), powłoka zinterpretuje ten łańcuch jako jedno długie słowo.

 1112
Author: liori,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-11 18:34:24

Zawsze zamieniam każdy osadzony pojedynczy cytat na sekwencję: '\'' (to jest: cudzysłów cudzysłów cudzysłów), która zamyka ciąg znaków, dodaje pojedynczy cytat i ponownie otwiera ciąg znaków.


Często uruchamiam funkcję "quotify" w skryptach Perla, aby zrobić to za mnie. Kroki będą:

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.
To załatwia wszystkie sprawy.

Życie staje się bardziej zabawne, gdy wprowadzasz eval do swoich skryptów powłoki. Zasadniczo musisz ponownie quotify znowu wszystko!

Na przykład Utwórz skrypt Perla o nazwie quotify zawierający powyższe instrukcje:

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

Następnie użyj go do wygenerowania poprawnie cytowanego ciągu znaków:

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

Wynik:

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

Które następnie można skopiować / wkleić do komendy alias:

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

(jeśli chcesz wstawić polecenie do evalu, uruchom ponownie quotify:

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

Wynik:

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

Które można skopiować/wkleić do evalu:

eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
 204
Author: Adrian Pronk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-06 20:44:43

Od Bash 2.04 składnia $'string' (zamiast tylko 'string'; uwaga: nie mylić z $('string')) jest innym mechanizmem cytowania, który umożliwia sekwencje escape podobne do ANSI C i rozszerzenie do wersji z pojedynczym cytowaniem.

Prosty przykład:

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

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

W Twoim przypadku:

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

Typowe sekwencje ucieczki działają zgodnie z oczekiwaniami:

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

Poniżej znajduje się Kopia + wklejona dokumentacja z man bash (Wersja 4.4):

Słowa postaci $'string' są traktowane specjalnie. Słowo rozszerza się na ciąg znaków, z odwrotnym ukośnikiem-znakami ucieczki zastąpionymi zgodnie ze standardem ANSI C. Sekwencje escape odwrotnego ukośnika, jeśli są obecne, są dekodowane w następujący sposób:

    \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

Wynik Rozszerzony jest pojedynczo cytowany, tak jakby znak dolara nie był obecny.


Zobacz Cytaty i znaki specjalne: ANSI C like strings on bash-hackers.org wiki po więcej szczegółów. Zauważ również, że "Bash Changes" file (overview here ) wspomina wiele o zmianach i poprawkach błędów związanych z mechanizmem cytowania $'string'.

Według unix.stackexchange.com Jak używać znaków specjalnych jako normalnych? powinno działać (z pewnymi odmianami) w bash, zsh, mksh, ksh93 oraz FreeBSD i busybox sh.

 126
Author: mj41,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-14 05:03:49

Nie widzę wpisu na jego blogu (link pls?) ale zgodnie z gnu reference manual :

Zamykanie znaków w pojedynczych cudzysłowach ("') zachowuje literalną wartość każdy znak w cudzysłowie. A pojedynczy cytat może nie wystąpić między pojedyncze cytaty, nawet gdy poprzedzone są odwrotny ukośnik.

Więc bash nie zrozumie:

alias x='y \'z '

Jednak możesz to zrobić, jeśli otaczasz się podwójnymi cudzysłowami:

alias x="echo \'y "
> x
> 'y
 43
Author: Steve B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-11 06:04:49

Mogę potwierdzić, że użycie '\'' dla pojedynczego cudzysłowu wewnątrz pojedynczego cytowanego ciągu działa w Bash i można to wyjaśnić w taki sam sposób, jak argument "klejenie" z wcześniejszego wątku. Załóżmy, że mamy cytowany ciąg znaków: 'A '\''B'\'' C' (Wszystkie cytaty tutaj są pojedynczymi cudzysłowami). Jeśli zostanie przekazany do echo, wyświetli następujący komunikat: A 'B' C. W każdym '\'' pierwszy cudzysłów zamyka bieżący pojedynczy cytowany ciąg, następny \' przykleja pojedynczy cudzysłów do poprzedniego łańcucha (\' jest sposobem na określenie pojedynczy cytat bez rozpoczynania cytowanego ciągu), a ostatni cytat otwiera kolejny cytowany ciąg.

 26
Author: Mikhail at YugaByte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-08 22:38:30

Prosty przykład cytatów specjalnych w powłoce:

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

Robi się to poprzez skończenie już otwartego ('), umieszczenie ucieczki (\'), a następnie otwarcie kolejnego ('). Ta składnia działa dla wszystkich poleceń. To bardzo podobne podejście do pierwszej odpowiedzi.

 14
Author: kenorb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-28 20:59:23

Nie zajmuję się konkretnie kwestią cytowania, bo czasami rozsądne jest rozważenie alternatywnego podejścia.

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

Które można następnie nazwać jako:

rxvt 123456 654321

Idea jest taka, że możesz teraz nazywać to bez obawy o cytaty:

alias rxvt='rxvt 123456 654321'

Lub, jeśli z jakiegoś powodu musisz dołączyć # do wszystkich wywołań:

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

Które można następnie nazwać jako:

rxvt '#123456' '#654321'

Wtedy, oczywiście, pseudonim to:

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

(UPS, I guess I kind of czy adres cytowania:)

 11
Author: nicerobot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-20 04:10:36

Używam tylko kodów powłoki.. np. \x27 LUB \\x22 odpowiednio. Żadnych kłopotów, nigdy naprawdę.

 10
Author: Rob Jens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-16 16:15:06

Ponieważ nie można umieszczać pojedynczych cudzysłowów w pojedynczych cytowanych łańcuchach, najprostszą i najbardziej czytelną opcją jest użycie ciągu HEREDOC

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

alias rxvt=$command

W powyższym kodzie HEREDOC jest wysyłany do polecenia cat, A wyjście z niego jest przypisywane do zmiennej za pomocą zapisu zastępczego polecenia $(..)

Jest to bardzo ważne, ponieważ jest to bardzo ważne, ponieważ jest to bardzo ważne.]}
 8
Author: Nerrve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-12 09:53:27

Obie wersje działają, albo z konkatenacją poprzez użycie unikalnego pojedynczego znaku cudzysłowu ( \ ' ), albo z konkatenacją poprzez zamknięcie pojedynczego znaku cudzysłowu w podwójnych cudzysłowach ("'").

Autor pytania nie zauważył, że na końcu ostatniej próby ucieczki pojawił się dodatkowy cytat ( ' ):

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

Jak widać w poprzednim ładnym fragmencie sztuki ASCII / Unicode, po ostatnim pojedynczym cytacie ( \ ' ) następuje niepotrzebny pojedynczy cytat ('). Używanie zakreślacza składni, takiego jak obecny w Notepad++ , może okazać się bardzo pomocne.

To samo dotyczy innego przykładu, takiego jak poniższy:

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

Te dwa piękne przykłady aliasów pokazują w bardzo skomplikowany i zaciemniony sposób, w jaki plik może być wyrównany. Oznacza to, że z pliku z wieloma wierszami otrzymujesz tylko jeden wiersz z przecinkami i spacjami między zawartością poprzednich wierszy. Aby nadać sens poprzedniemu komentarzowi, poniżej znajduje się przykład:

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214
 8
Author: leftaroundabout,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-01 13:05:29

IMHO prawdziwa odpowiedź jest taka, że nie można uniknąć pojedynczych cudzysłowów w pojedynczo cytowanych ciągach.

To niemożliwe.

Jeśli Zakładamy, że używamy Basha.

Z podręcznika 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.

Musisz użyć jednego z innych mechanizmów string escape " lub \

Nie ma nic magicznego w alias, które wymagałoby użycia pojedynczych cudzysłowów.

Obie poniższe prace działają w bash.

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

Ten ostatni używa \ do ucieczki z przestrzeni charakter.

Nie ma również nic magicznego w #111111, co wymaga pojedynczych cudzysłowów.

Następujące opcje osiągają ten sam wynik co pozostałe dwie opcje, w tym, że alias rxvt działa zgodnie z oczekiwaniami.

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

Możesz również uciec od kłopotliwego # bezpośrednio

alias rxvt="urxvt -fg \#111111 -bg \#111111"
 6
Author: teknopaul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-09 23:47:57

Większość z tych odpowiedzi dotyczyła konkretnej sprawy, o którą pytasz. Istnieje ogólne podejście, które przyjaciel i ja opracowaliśmy, które pozwala na dowolne cytowanie w przypadku, gdy trzeba cytować polecenia bash przez wiele warstw rozszerzenia powłoki, np. przez ssh, su -c, bash -c, itd. Jest jeden rdzeń prymitywny, którego potrzebujesz, tutaj w natywnym 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
}

To robi dokładnie to, co mówi: Shell-cytuje każdy argument indywidualnie (po rozszerzeniu bash, z kurs):

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

Robi oczywistą rzecz dla jednej warstwy ekspansji:

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

(zauważ, że podwójne cudzysłowy wokół $(quote_args ...) są niezbędne do przekształcenia wyniku w pojedynczy argument bash -c.) I może być używany bardziej ogólnie do prawidłowego cytowania poprzez wiele warstw ekspansji:

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

Powyższy przykład:

  1. shell-cytuje każdy argument do wewnętrznego quote_args indywidualnie, a następnie łączy wynikowe wyjście w jeden argument z wewnętrzne podwójne cudzysłowy.
  2. Shell-cytaty bash, -c, i już raz cytowany wynik z kroku 1, a następnie łączy wynik w jeden argument z zewnętrznymi podwójnymi cudzysłowami.
  3. wysyła ten bałagan jako argument do zewnętrznego bash -c.
To jest pomysł w skrócie. Można z tym zrobić dość skomplikowane rzeczy, ale trzeba uważać na kolejność oceny i na to, które podcięcia są cytowane. Na przykład, następujące zrobić złe rzeczy (dla pewnej definicji "źle"):
$ (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

W pierwszym przykładzie bash natychmiast rozszerza quote_args cd /; pwd 1>&2 na dwa oddzielne polecenia, quote_args cd / i pwd 1>&2, więc CWD jest nadal /tmp, gdy wykonywane jest polecenie pwd. Drugi przykład ilustruje podobny problem globbingu. Rzeczywiście, ten sam podstawowy problem występuje we wszystkich rozszerzeniach bash. Problem polega na tym, że zastępowanie poleceń nie jest wywołaniem funkcji: dosłownie ocenia jeden skrypt bash i wykorzystuje jego wyjście jako część innego skrypt bash.

Jeśli spróbujesz po prostu uciec operatorom powłoki, nie uda Ci się, ponieważ wynikowy ciąg znaków przekazany do bash -c jest tylko sekwencją pojedynczo cytowanych łańcuchów, które nie są następnie interpretowane jako operatory, co jest łatwe do sprawdzenia, jeśli powtórzysz łańcuch, który byłby przekazany do Basha:

$ (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'
Problem w tym, że przesadzasz z cytowaniem. To, czego potrzebujesz, to aby operatory nie były cytowane jako dane wejściowe do enclosing bash -c, co oznacza, że muszą być poza $(quote_args ...) zastępowanie poleceń.

W związku z tym, co musisz zrobić w najbardziej ogólnym sensie, to zacytować każde słowo polecenia, które nie ma być rozszerzone w czasie zastępowania komendy osobno, i nie stosować żadnych dodatkowych cytowań do operatorów powłoki:

$ (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

Gdy już to zrobisz, cały ciąg jest sprawiedliwy dla dalszego cytowania do arbitralnych poziomów oceny:

$ 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

Itd.

Te przykłady mogą wydawać się przesadzone, biorąc pod uwagę, że słowa jak success, sbin, i pwd nie muszą być cytowane przez powłokę, ale kluczową kwestią, którą należy pamiętać podczas pisania skryptu pobierającego dowolne dane wejściowe, jest to, że chcesz cytować wszystko, czego nie jesteś absolutnie pewien nie potrzebuje cytowania, ponieważ nigdy nie wiesz, kiedy użytkownik dorzuciRobert'; rm -rf /.

Aby lepiej zrozumieć, co dzieje się pod pokrywami, możesz bawić się dwoma małymi funkcjami pomocniczymi:]}

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

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

Który wyliczy każdy argument do polecenia przed wykonanie:

$ 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
 5
Author: Kyle Rose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-30 13:46:15

W podanym przykładzie po prostu użyto podwójnych cudzysłowów zamiast pojedynczych cudzysłowów jako zewnętrznego mechanizmu ucieczki:

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

To podejście jest odpowiednie dla wielu przypadków, w których chcesz przekazać stały ciąg do polecenia: po prostu sprawdź, jak powłoka zinterpretuje dwukrotnie cytowany ciąg za pomocą echo, a w razie potrzeby użyj znaków specjalnych z odwrotnym ukośnikiem.

W przykładzie widać, że podwójne cudzysłowy są wystarczające do ochrony łańcucha znaków:

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'
 4
Author: oberlies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-25 14:36:57

Oto opracowanie jednej prawdziwej odpowiedzi, o której mowa powyżej:

Czasami będę pobierał za pomocą rsync przez ssh i będę musiał uciec nazwę pliku z ' w nim dwa razy! (OMG!) Raz na bash i raz na ssh. Ta sama zasada przemiennych ograniczników cudzysłowu działa tutaj.

Na przykład, powiedzmy, że chcemy uzyskać: historie Louisa Theroux ' a ...

  1. najpierw załączasz Louis Theroux w pojedynczych cudzysłowach dla bash i podwójnych cudzysłowach dla ssh: ""Louis Theroux "'
  2. Następnie używasz pojedynczych cudzysłowów, aby uniknąć podwójnego cudzysłowu ""
  3. użyj podwójnych cudzysłowów, aby uciec apostrofu "'"
  4. następnie powtórz #2, używając pojedynczych cudzysłowów, aby uniknąć podwójnego cudzysłowu ""
  5. następnie załącz LA Stories w pojedynczych cudzysłowach dla bash i podwójnych cudzysłowach dla ssh: '"La Stories "'

I oto! Kończysz z tym:

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

Co jest strasznie dużo pracy dla jednego małego ' -- ale proszę bardzo

 3
Author: PatchyFog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-07 23:39:14

Inny sposób na rozwiązanie problemu zbyt wielu warstw zagnieżdżonego notowania:

Próbujesz wcisnąć zbyt dużo w zbyt małą przestrzeń, więc użyj funkcji bash.

Problem polega na tym, że próbujesz mieć zbyt wiele poziomów zagnieżdżania, a podstawowa technologia aliasów nie jest wystarczająco potężna, aby ją pomieścić. Użyj funkcji bash takiej jak ta, aby uczynić ją tak, aby pojedyncze, podwójne cudzysłowy i przekazane w parametrach były obsługiwane normalnie, jak byśmy chcieli oczekujemy:

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

Wtedy możesz użyć zmiennych $1 i $2 oraz pojedynczych, podwójnych cudzysłowów i tylnych wskazów, nie martwiąc się o to, że funkcja alias zniszczy ich integralność.

Ten program wyświetla:

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------
 2
Author: Eric Leschinski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-27 00:43:46

Ta funkcja:

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

Umożliwia cytowanie ' wewnątrz '. Użyj jako tego:

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

Jeśli linia do cytowania staje się bardziej złożona, jak podwójne cudzysłowy zmieszane z pojedynczymi cudzysłowami, może być dość trudne uzyskanie ciągu do cytowania wewnątrz zmiennej. Gdy pojawią się takie przypadki, napisz dokładny wiersz, który musisz zacytować wewnątrz skryptu (podobny do tego).

#!/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_

Wyświetli:

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

Wszystkie poprawnie cytowane ciągi wewnątrz pojedynczych cudzysłowów.

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-21 11:01:15

Oczywiście łatwiej byłoby po prostu otoczyć podwójnymi cudzysłowami, ale gdzie w tym tkwi wyzwanie? Oto odpowiedź, używając tylko pojedynczych cudzysłowów. Używam zmiennej zamiast alias, więc łatwiej jest wydrukować dla dowodu, ale jest to to samo, co użycie alias.

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

Wyjaśnienie

Kluczem jest to, że możesz zamknąć pojedynczy cytat i ponownie otworzyć go tyle razy, ile chcesz. Na przykład foo='a''b' jest tym samym co foo='ab'. Więc możesz zamknąć pojedynczy cytat, dorzucić literalny pojedynczy cytat \', a następnie ponownie Otwórz Następny pojedynczy cytat.

Schemat podziału

Ten diagram wyjaśnia, używając nawiasów do pokazania, gdzie pojedyncze cudzysłowy są otwierane i zamykane. Cudzysłowy nie są "zagnieżdżane", tak jak nawiasy mogą być. Możesz również zwrócić uwagę na podświetlenie kolorów, które jest prawidłowo zastosowane. Cytowane ciągi są bordowe, natomiast {[6] } jest czarne.

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

(jest to zasadniczo ta sama odpowiedź co Adrian, ale czuję, że to wyjaśnia lepiej. Również jego odpowiedź ma 2 zbędne pojedyncze cytaty na końcu.)

 1
Author: wisbucky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-02 04:53:27

Oto inne rozwiązanie. Funkcja ta pobierze pojedynczy argument i odpowiednio zacytuje go za pomocą znaku jednokrotnego cytowania, tak jak powyższa odpowiedź wyjaśnia:

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

Więc możesz go użyć w ten sposób:

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

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
 0
Author: exbuddha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-27 18:41:09

Jeśli generujesz łańcuch powłoki w Pythonie 2 lub Pythonie 3, poniższe argumenty mogą pomóc w zacytowaniu:

#!/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()]))

Wyświetli się:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
 0
Author: George V. Reilly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-22 20:58:48