Come sfuggire alle virgolette singole all'interno di stringhe tra virgolette singole

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

  •  12-09-2019
  •  | 
  •  

Domanda

Diciamo che hai un Bash alias Piace:

alias rxvt='urxvt'

che funziona bene.

Tuttavia:

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

non funzionerà, e nemmeno:

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

Allora come si fa a far corrispondere le virgolette di apertura e di chiusura all'interno di una stringa una volta sfuggite alle virgolette?

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

sembra sgraziato anche se rappresenterebbe la stessa stringa se ti fosse permesso concatenarli in quel modo.

È stato utile?

Soluzione

Se davvero si vuole utilizzare le virgolette singole nello strato più esterno, si ricordi che è possibile incollare entrambi i tipi di quotazione. Esempio:

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

Spiegazione di come '"'"' viene interpretato come solo ':

  1. ' Fine prima citazione che usa le virgolette singole.
  2. Avvia " seconda citazione, usando virgolette.
  3. ' carattere Citato.
  4. " Fine seconda citazione, usando virgolette.
  5. ' Comincia terza citazione, usando le virgolette singole.

Se non si inserisce alcun spazi bianchi tra (1) e (2), o tra (4) e (5), la shell interpreta la stringa come una parola una lunga.

Altri suggerimenti

ho sempre solo sostituire ogni singola citazione embedded con la sequenza: '\'' (vale a dire: quote backslash quote quote) che chiude la stringa, aggiunge un apostrofo sfuggito e riapre la stringa.


Mi capita spesso di improvvisare una funzione di "quotify" nei miei script Perl di fare questo per me. sarebbero i passi:

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

Questa operazione richiede più o meno la cura di tutti i casi.

La vita diventa più divertente quando si introducono eval nella vostra shell-script. Bisogna essenzialmente ri-quotify di nuovo tutto!

Ad esempio, creare uno script Perl chiamato quotify contenente le dichiarazioni di cui sopra:

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

quindi utilizzarlo per generare una stringa correttamente citato:

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

Risultati:

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

che possono poi essere copia / incollato nel comando alias:

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

(Se è necessario inserire il comando in un eval, eseguire nuovamente il quotify:

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

Risultati:

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

che può essere copia / incollato in un eval:

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

Da Bash 2.04 sintassi $'string' (invece di 'string'; avvertimento: non confondere con $('string')) è un altro meccanismo che permette citare sequenze ANSI C-like fuga e fanno espansione versione single-citato.

Un semplice esempio:

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

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

Nel tuo caso:

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

Comune sfuggire sequenze funziona come previsto:

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

Di seguito è copia + documentazione relativa incollato da man bash (versione 4.4):

Le parole della forma $ 'stringa' sono trattati in modo speciale. La parola espansa alla stringa, con barre rovesciate escape sostituiti come specificato dallo standard ANSI C. sequenze di escape backslash, se presenti, vengono decodificati come segue:

    \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

Il risultato espanso è unico citato, come se il simbolo del dollaro non era stato presente.


Citazioni e fuggire: ANSI C come stringhe su bash-hacker. wiki org per maggiori dettagli. Si noti inoltre che il file "Bash Changes" ( qui ) cita un sacco di modifiche e correzioni di bug relativi al meccanismo citando $'string'.

Secondo unix.stackexchange.com come utilizzare un carattere speciale come uno normale? dovrebbe funzionare (con alcune variazioni) in bash, zsh, mksh, ksh93 e FreeBSD e busybox sh.

Non vedo l'entrata sul suo blog (link pls?), Ma in base al GNU manuale di riferimento :

  

Racchiudere caratteri in virgolette singole   ( ‘'’) Conserva il valore letterale di   ogni personaggio all'interno delle virgolette. UN   apostrofo non può verificarsi tra   virgolette singole, anche se preceduti da un   backslash.

in modo da bash non capirà:

alias x='y \'z '

Tuttavia, si può fare questo se si circondi di virgolette:

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

Posso confermare che l'utilizzo '\'' per una singola citazione all'interno di una stringa tra apici singoli funziona in Bash, e può essere spiegato nello stesso modo come l'argomento "incollaggio" da prima nel thread. Supponiamo di avere una stringa tra virgolette: 'A '\''B'\'' C' (tutte le citazioni qui sono virgolette singole). Se si passa ad echeggiare, stampa il seguente: A 'B' C. In ogni '\'' prima citazione chiude la corrente di stringa apici singoli, le seguenti colle \' una sola offerta alla stringa precedente (\' è un modo per specificare un singolo preventivo senza avviare una stringa citata), e l'ultima citazione apre un altro singolo stringa tra virgolette.

Semplice esempio di fuga citazioni in guscio:

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

E 'fatto da finitura già aperto uno ('), ponendo scampato (\'), poi l'apertura di un altro ('). Questa sintassi funziona per tutti i comandi. E 'approccio molto simile alla prima risposta.

Entrambe le versioni sono al lavoro, sia con la concatenazione utilizzando il carattere preventivo singolo escape (\ '), oppure con la concatenazione racchiudendo il singolo carattere citazione tra virgolette ( "'").

L'autore della questione non si accorse che c'era un preventivo singolo ( ') alla fine del suo ultimo tentativo fuga:

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

Come si può vedere nel precedente bel pezzo di ASCII / Unicode arte, l'ultimo sfuggito apice singolo (\ ') è seguita da un apostrofo non necessaria ('). Usando una sintassi evidenziatore come quello presente in Notepad ++ può rivelarsi molto utile.

Lo stesso vale per un altro esempio come la seguente:

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

Queste due belle istanze di alias mostrano in modo molto intricato e offuscato come un file può essere allineato verso il basso. Cioè, da un file con un sacco di linee che si ottiene solo una linea con le virgole e gli spazi tra i contenuti delle linee precedenti. Al fine di dare un senso al commento precedente, il seguente è un esempio:

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214

Non sto specificamente affrontare la questione citando perché, beh, a volte, è solo ragionevole considerare un approccio alternativo.

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

che è quindi possibile chiamare come:

rxvt 123456 654321

l'idea è che ora è possibile alias questo senza preoccupazione per le citazioni:

alias rxvt='rxvt 123456 654321'

o, se è necessario includere il # a tutte le chiamate per qualche motivo:

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

che è quindi possibile chiamare come:

rxvt '#123456' '#654321'

poi, naturalmente, un alias è:

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

(oops, penso che tipo di ha affrontato la citazione:)

mi basta usare codici shell .. per esempio \x27 o \\x22 come applicabile. Nessun problema, mai veramente.

Dal momento che non si può mettere le virgolette singole all'interno di singole stringhe tra virgolette, l'opzione più semplice e più leggibile è quello di utilizzare una stringa heredoc

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

alias rxvt=$command

Nel codice precedente, il heredoc viene inviato al comando cat e viene assegnato l'uscita di questo ad una variabile tramite il comando di sostituzione notazione $(..)

Mettere una sola offerta in tutto il heredoc è necessaria in quanto è all'interno di un $()

IMHO la vera risposta è che non si può sfuggire apici singoli all'interno di stringhe racchiuse tra virgolette singole.

La sua impossibile.

Se supponiamo che stiamo usando bash.

Da manuale 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.

È necessario utilizzare uno degli altri meccanismi di stringa di fuga "o \

Non c'è nulla di magico alias che lo richiede utilizzare le virgolette singole.

Sia i seguenti lavori in bash.

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

Il secondo usa \ per sfuggire il carattere di spazio.

C'è anche nulla di magico # 111111 che richiede virgolette singole.

Le opzioni seguenti ottiene lo stesso risultato delle altre due opzioni, in quanto l'alias rxvt funziona come previsto.

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

Si può anche sfuggire al fastidioso # direttamente

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

La maggior parte di queste risposte ha colpito sul caso specifico che stai chiedendo. V'è un approccio generale che un amico e ho sviluppato che consente arbitrario citare in caso di necessità di citare comandi bash attraverso strati multipli di espansione shell, ad esempio, attraverso ssh, su -c, bash -c, ecc C'è un nucleo primitivo è necessario , qui in bash completo:

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
}

Questo fa esattamente quello che dice: shell virgolette ogni argomento individualmente (dopo l'espansione bash, ovviamente):

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

Si fa la cosa più ovvia per uno strato di espansione:

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

(Si noti che le virgolette intorno $(quote_args ...) sono necessari per rendere il risultato in un singolo argomento per bash -c.) E può essere utilizzato più in generale per citare correttamente attraverso livelli multipli di espansione:

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

L'esempio sopra:

  1. shell apici ogni argomento al quote_args interno individualmente e quindi combina l'uscita risultante in un singolo argomento con le virgolette interne.
  2. shell apici bash, -c, e il risultato già una volta citata dal passaggio 1, e quindi combina il risultato in un singolo argomento con le virgolette esterne.
  3. invia quel pasticcio come argomento al bash -c esterno.

Questa è l'idea in poche parole. Si può fare alcune cose piuttosto complicato con questo, ma bisogna stare attenti a ordine di valutazione e di cui stringhe sono quotati. Per esempio, il seguente fare le cose sbagliate (per qualche definizione di "sbagliato"):

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

Nel primo esempio, bash espande immediatamente quote_args cd /; pwd 1>&2 in due comandi distinti, quote_args cd / e pwd 1>&2, così il CWD è ancora /tmp quando viene eseguito il comando pwd. Il secondo esempio illustra un problema simile per globbing. In effetti, lo stesso problema di base si verifica con tutte le espansioni bash. Il problema è che una sostituzione di comando non è una chiamata di funzione. È letteralmente valutare uno script bash e utilizza la sua produzione come parte di un altro script bash

Se si tenta di sfuggire semplicemente gli operatori di shell, fallirai perché la stringa risultante passata a bash -c è solo una sequenza di stringhe singolarmente quotati che non sono poi interpretati come operatori, che è facile da vedere se si echo la stringa che sarebbe stato passato a 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'

Il problema qui è che sei troppo citando. Quello che vi serve è per gli operatori da non quotate come input per il bash -c racchiude, il che significa che hanno bisogno di essere al di fuori della sostituzione di comando $(quote_args ...).

Di conseguenza, ciò che è necessario fare nel senso più generale è a sborsare-quote ogni parola del comando non destinato ad essere ampliato al momento della sostituzione di comando a parte, e non applicare alcuna ulteriore citando agli operatori shell:

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

Una volta fatto questo, l'intera stringa è un gioco equo per ulteriori citando a livelli arbitrari di valutazione:

$ 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

ecc.

Questi esempi possono sembrare esagerata dato che parole come success, sbin, e pwd non hanno bisogno di essere shell citato, ma il punto fondamentale da ricordare quando si scrive una sceneggiatura prendendo ingresso arbitrario è che si vuole citare tutto ciò che si' re non è assolutamente sicuro che non bisogno citando, perché non si sa mai quando un utente getterà in un Robert'; rm -rf /.

Per capire meglio cosa sta succedendo sotto le coperte, si può giocare con due piccole funzioni di supporto:

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

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

che enumerare ogni argomento ad un comando prima di eseguirlo:

$ 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

Nell'esempio dato, utilizzato semplicemente virgolette anziché i singoli apici come meccanismo di fuga esterno:

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

Questo approccio è adatto per molti casi in cui si desidera solo per passare una stringa fissa a un comando:. Basta controllare come la shell interpreterà la stringa con virgolette doppie attraverso un echo, e la fuga caratteri con backslash se necessario

In questo esempio, si vedrebbe che le doppie virgolette sono sufficienti per proteggere la stringa:

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

Ecco un'elaborazione su The One vera risposta di cui sopra:

A volte mi Scaricherò utilizzando rsync su SSH e devono scappare un nome di file con un 'in due volte! (OMG!) Una volta per bash e una volta per ssh. Lo stesso principio di alternanza delimitatori di quotazione è al lavoro qui.

Per esempio, diciamo che vogliamo ottenere: LA Storie di Louis Theroux ...

  1. Per prima cosa è racchiudere Louis Theroux in singoli apici per bash e doppi apici per ssh: ' "Louis Theroux"'
  2. Quindi si utilizza virgolette singole per sfuggire a una doppia citazione '"'
  3. Le virgolette doppie uso per sfuggire l'apostrofo "'"
  4. Quindi ripetere 2 #, utilizzando le virgolette singole per sfuggire a una doppia citazione '"'
  5. Poi racchiudere LA Storie di singoli preventivi per bash e doppi apici per ssh: ' 'La Storie''

Ed ecco! Si finisce con questo:

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

, che è un sacco di lavoro per un po '- ma ci si va

Un altro modo per risolvere il problema dei troppi strati di citazione nidificato:

Si sta tentando di stipare troppo in troppo piccoli uno spazio, in modo da utilizzare una funzione di bash.

Il problema è che si sta cercando di avere troppi livelli di nidificazione, e la tecnologia di base alias non è abbastanza potente per ospitare. Utilizzare una funzione bash come questo per fare in modo le virgolette doppie, singole indietro zecche e passato in parametri sono tutti trattati normalmente come ci aspetteremmo:

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

Quindi è possibile utilizzare i $ 1 e $ 2 variabili e, doppi apici singoli e ritorno zecche senza preoccuparsi della funzione di alias distruggendo la loro integrità.

stampe questo programma:

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

Spiegazione dell'implementazione:

  • virgolette doppie in modo da poter facilmente produrre il wrapper di virgolette singole e utilizzare il file ${...} sintassi

  • la ricerca e sostituzione di bash è simile a: ${varname//search/replacement}

  • stiamo sostituendo ' con '\''

  • '\'' codifica un singolo ' così:

    1. ' termina la citazione singola

    2. \' codifica a ' (la barra rovesciata è necessaria perché non siamo racchiusi tra virgolette)

    3. ' ricomincia la virgolettatura singola

    4. bash concatena automaticamente le stringhe senza spazi bianchi in mezzo

  • c'è un \ prima di ogni \ E ' perché queste sono le regole di fuga per ${...//.../...} .

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

PSCodifica sempre in stringhe con virgolette singole perché sono molto più semplici delle stringhe con virgolette doppie.

Se avete GNU parallelo installato è possibile utilizzare il suo interno citando:

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

Dalla versione 20190222 si può anche --shellquote più volte:

$ 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

Sarà citare la stringa in tutti i gusci supportate (non solo bash).

Questa funzione:

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

permette di citando ' all'interno '. Utilizzare come questo:

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

Se la linea di citazione diventa più complesso, come le virgolette mescolato con virgolette singole, può diventare molto difficile da ottenere la stringa di citare all'interno di una variabile. Quando tali casi appaiono, prego scrivere la linea esatta che è necessario citare all'interno di uno script (simile a questo).

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

output sarà:

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

Tutte le stringhe correttamente citati all'interno di virgolette singole.

Ovviamente, sarebbe più facile semplicemente quello di circondare con le doppie virgolette, ma dov'è la sfida in questo? Ecco la risposta utilizzando quotazioni solo singoli. Sto utilizzando una variabile al posto di alias così che è è più facile da stampare per la prova, ma è lo stesso che utilizzare alias.

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

Spiegazione

La chiave è che si può chiudere il singolo preventivo e riaprire tutte le volte che vuoi. Ad esempio foo='a''b' è la stessa foo='ab'. Così si può chiudere l'apice singolo, gettare in un letterale \' apostrofo, quindi riaprire il prossimo singolo preventivo.

Schema Breakdown

Questo diagramma chiarisce utilizzando staffe per mostrare dove i singoli apici vengono aperte e chiuse. Citazioni non sono "annidati" come parentesi possono essere. Si può anche prestare attenzione al colore evidenziazione, che viene applicato correttamente. Le stringhe tra virgolette sono marrone, mentre il \' è nero.

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

(Questo è essenzialmente la stessa risposta di Adrian, ma sento che questo spiega meglio. Anche la sua risposta ha 2 virgolette singole superflui alla fine).

Ecco un'altra soluzione. Questa funzione avrà un singolo argomento e in modo appropriato citare utilizzando il singolo carattere-citazione, proprio come la risposta votato sopra spiega:

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

Quindi, è possibile utilizzare in questo modo:

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

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

Se si sta generando la stringa shell all'interno di Python 2 o Python 3, possono aiutare a citare gli argomenti:

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

Questa uscita volontà:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top