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: Peter Mortensen, 2009-08-08

24 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.

 1569
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'\''\'\'''\'''\'''
 286
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.

 221
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
 49
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.

 35
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

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
 21
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

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.

 16
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:)

 16
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

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.]}
 12
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

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

 11
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

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

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.)

 4
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 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
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

Wyjaśnienie realizacji:

  • Podwójne cudzysłowy, dzięki czemu możemy łatwo wypisywać pojedyncze cudzysłowy i używać składni ${...}

  • Wyszukiwanie i zamiana Basha wygląda tak: ${varname//search/replacement}

  • Zamieniamy ' na '\''

  • '\'' koduje pojedynczy ' w ten sposób:

    1. ' Kończy pojedynczy cytat

    2. \' koduje ' (backslash jest potrzebny, ponieważ nie jesteśmy w środku cytaty)

    3. ' starts up single quoting again

    4. Bash automatycznie łączy ciągi bez spacji pomiędzy

  • Przed każdym \ i ' jest \, ponieważ takie są zasady ucieczki ${...//.../...}.

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

P. S. zawsze koduj pojedyncze cytowane ciągi, ponieważ są one o wiele prostsze niż podwójne cytowane ciągi.

 3
Author: JasonWoof,
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
2019-06-10 02:43:32

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

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!'
 2
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

Jeśli masz zainstalowany GNU Parallel możesz użyć jego wewnętrznego cytowania:

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

Od wersji 20190222 możesz nawet --shellquote wiele razy:

$ 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

Zacytuje łańcuch we wszystkich obsługiwanych powłokach(nie tylko bash).

 2
Author: Ole Tange,
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
2019-07-11 05:48:23

Oto moje dwa grosze-w przypadku, jeśli ktoś chce być sh - przenośny, a nie tylko bash-specyficzny ( rozwiązanie nie jest jednak zbyt wydajne, ponieważ uruchamia zewnętrzny program -- sed ):

  • Wstaw to quote.sh (lub po prostu quote) gdzieś na twoim PATH :
# this works with standard input (stdin)
quote() {
  echo -n "'" ;
  sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' ;
  echo -n "'"
}

case "$1" in
 -) quote ;;
 *) echo "usage: cat ... | quote - # single-quotes input for Bourne shell" 2>&1 ;;
esac

Przykład:

$ echo -n "G'day, mate!" | ./quote.sh -
'G'"'"'day, mate!'

I, oczywiście, że wraca:

$ echo 'G'"'"'day, mate!'
G'day, mate!

Explanation: zasadniczo musimy załączyć dane wejściowe cudzysłowami ', a następnie również zastąpić każdy pojedynczy cytat wewnątrz tego mikro-potwora: '"'"' (Zakończ otwierający cytat pairingiem ', ucieknij od znalezionego pojedynczego cytatu, owijając go podwójnymi cudzysłowami -- "'", a następnie wydaj nowy otwierający pojedynczy cytat ', lub w pseudo-notacji : ' + "'" + ' == '"'"' )

Standardowym sposobem na to jest użycie sed z następującym poleceniem substytucji:

s/\(['][']*\)/'"\1"'/g 

Mały problem polega jednak na tym, że aby użyć go w powłoce, trzeba uciec od wszystkich tych pojedynczych cytowanie znaków w samym wyrażeniu sed - co prowadzi do czegoś takiego jak

sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' 

(a dobrym sposobem na zbudowanie tego wyniku jest zasilenie oryginalnego wyrażenia s/\(['][']*\)/'"\1"'/g skryptami Kyle' a Rose 'a lub George' a V. Reilly ' ego).

Wreszcie, ma sens oczekiwać, że dane wejściowe będą pochodzić z stdin - ponieważ przekazywanie ich przez argumenty wiersza poleceń może być już zbyt dużym problemem.

( Oh, I być może chcemy dodać mały komunikat pomocy, aby skrypt nie zawisał kiedy ktoś po prostu uruchamia go jako ./quote.sh --help zastanawiając się, co to robi. )

 2
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
2020-01-31 10:44:58

Oprócz @ JasonWoof doskonała odpowiedź chcę pokazać, jak rozwiązałem pokrewny problem

W moim przypadku kodowanie pojedynczych cudzysłowów za pomocą '\'' nie zawsze będzie wystarczające, na przykład jeśli ciąg znaków musi być cytowany z pojedynczymi cudzysłowami, ale całkowita liczba cytatów skutkuje nieparzystą ilością

#!/bin/bash

# no closing quote
string='alecxs\'solution'

# this works for string
string="alecxs'solution"
string=alecxs\'solution
string='alecxs'\''solution'

Załóżmy, że string jest nazwą pliku i musimy zapisać cytowane nazwy plików na liście (jak stat-c%n ./ * > list )

echo "'$string'" > "$string"
cat "$string"

Ale przetwarzanie tej listy nie powiedzie się (w zależności ile cudzysłowów zawiera w sumie łańcuch znaków)

while read file
  do
    ls -l "$file"
    eval ls -l "$file"
done < "$string"

Obejście: kodowanie cudzysłowów za pomocą manipulacji łańcuchem znaków

string="${string//$'\047'/\'\$\'\\\\047\'\'}"

# result
echo "$string"

Teraz to działa, ponieważ cytaty są zawsze zrównoważone

echo "'$string'" > list
while read file
  do
    ls -l "$file"
    eval ls -l "$file"
done < list
Mam nadzieję, że to pomoże w obliczu podobnego problemu]}
 2
Author: alecxs,
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
2020-06-14 16:25:03

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

Jak uciec pojedyncze cudzysłowy (') i podwójne cudzysłowy (") z hex i ósemkowe } znaki

Jeśli używam czegoś takiego jak echo, miałem kilka naprawdę skomplikowanych, naprawdę dziwnych i trudnych do ucieczki (pomyśl: bardzo zagnieżdżonych) przypadków, w których jedyną rzeczą, którą mogłem dostać się do pracy, było używanie ósemkowych lub szesnastkowych kodów!

Oto kilka przykładów:

1. Przykład pojedynczego cytowania, gdzie ' jest unikalny przez hex \x27 lub oktal \047 (jego odpowiedniki kod ASCII):

# 1. hex
$ echo -e "Let\x27s get coding!"
Let's get coding!

# 2. octal
$ echo -e "Let\047s get coding!"
Let's get coding!

2. Przykład podwójnego cytowania, gdzie " jest unikalny przez hex \x22 lub oktal \042 (jego odpowiedni kod ASCII ).

Uwaga: bash to szaleństwo! czasami nawet znak ! ma specjalne znaczenie I musi być albo usunięty z cudzysłowów podwójnych, a następnie uciekany "like this"\! lub umieszczony całkowicie w pojedynczych cudzysłowach 'like this!', a nie w podwójnych cudzysłowach.

# 1. hex; escape `!` by removing it from within the double quotes 
# and escaping it with `\!`
$ echo -e "She said, \x22Let\x27s get coding"\!"\x22"
She said, "Let's get coding!"

# OR put it all within single quotes:
$ echo -e 'She said, \x22Let\x27s get coding!\x22'
She said, "Let's get coding!"


# 2. octal; escape `!` by removing it from within the double quotes 
$ echo -e "She said, \042Let\047s get coding"\!"\042"
She said, "Let's get coding!"

# OR put it all within single quotes:
$ echo -e 'She said, \042Let\047s get coding!\042'
She said, "Let's get coding!"


# 3. mixed hex and octal, just for fun
# escape `!` by removing it from within the double quotes when it is followed by
# another escape sequence
$ echo -e "She said, \x22Let\047s get coding! It\x27s waaay past time to begin"\!"\042"
She said, "Let's get coding! It's waaay past time to begin!"

# OR put it all within single quotes:
$ echo -e 'She said, \x22Let\047s get coding! It\x27s waaay past time to begin!\042'
She said, "Let's get coding! It's waaay past time to begin!"

Zauważ, że jeśli nie uciekaj właściwie !, gdy zajdzie taka potrzeba, jak pokazałem powyżej dwa sposoby, pojawią się dziwne błędy, takie jak:

$ echo -e "She said, \x22Let\047s get coding! It\x27s waaay past time to begin!\042"
bash: !\042: event not found

Lub:

$ echo -e "She said, \x22Let\x27s get coding!\x22"
bash: !\x22: event not found

Bibliografia:

  1. https://en.wikipedia.org/wiki/ASCII#Printable_characters
  2. https://serverfault.com/questions/208265/what-is-bash-event-not-found/208266#208266
  3. Zobacz także moją drugą odpowiedź tutaj: jak pisać znaki spoza ASCII używając echo?.
 1
Author: Gabriel Staples,
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
2021-01-25 04:36:22

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