Esci da una variabile da utilizzare come contenuto di un altro script


20

Questa domanda non riguarda come scrivere letteralmente una stringa con escape. Non sono riuscito a trovare alcuna domanda correlata che non riguardi come sfuggire alle variabili per il consumo diretto all'interno di uno script o di altri programmi.

Il mio obiettivo è abilitare uno script per generare altri script. Questo perché le attività negli script generati verranno eseguite ovunque da 0 a n volte su un altro computer e i dati da cui vengono generati potrebbero cambiare prima di essere eseguiti (di nuovo), quindi eseguendo le operazioni direttamente, su una rete non funziona.

Data una variabile nota che può contenere caratteri speciali come virgolette singole, devo scriverla come una stringa letteralmente completamente sfuggita, ad esempio una variabile foocontenente bar'bazdovrebbe apparire nello script generato come:

qux='bar'\''baz'

che verrebbe scritto aggiungendo "qux=$foo_esc"alle altre righe della sceneggiatura. L'ho fatto usando Perl in questo modo:

foo_esc="'`perl -pe 's/('\'')/\\1\\\\\\1\\1/g' <<<"$foo"`'"

ma questo sembra eccessivo.

Non ho avuto successo nel farlo con bash da solo. Ho provato molte varianti di questi:

foo_esc="'${file//\'/\'\\\'\'}'"
foo_esc="'${file//\'/'\\''}'"

ma nell'output compaiono delle barre extra (quando lo faccio echo "$foo") o causano un errore di sintassi (aspettandosi ulteriori input se fatto dalla shell).


aliase / o setabbastanza universalmente
mikeserv il

@mikeserv Siamo spiacenti, non sono sicuro di cosa intendi.
Walf,

alias "varname=$varname" varnameoppurevar=value set
mikeserv,

2
@mikeserv Questo non è un contesto sufficiente per me per capire cosa dovrebbe fare il tuo suggerimento, né come lo userei come metodo generico per sfuggire a qualsiasi variabile. È un problema risolto, amico.
Walf

Risposte:


26

Bash ha un'opzione di espansione dei parametri proprio per questo caso :

${parameter@Q}L'espansione è una stringa che è il valore del parametro citato in un formato che può essere riutilizzato come input.

Quindi in questo caso:

foo_esc="${foo@Q}"

Questo è supportato in Bash 4.4 e versioni successive. Esistono diverse opzioni anche per altre forme di espansione e per la generazione specifica di istruzioni di assegnazione complete ( @A).


7
Pulito, ma solo 4.2 che dà bad substitution.
Walf,

3
L'equivalente della shell Z è "${foo:q}".
JdeBP,

Salvami davvero la vita! "${foo@Q}"lavori!
hao

@JdeBP che l'equivalente della shell Z non funziona. Altre idee per zsh?
Steven Shaw,

1
Ho trovato la risposta: "$ {(@ qq) foo}"
Steven Shaw,

10

Bash fornisce un printfintegrato con %qdi formato, che svolge la Shell fuga per voi, anche in più vecchi (<4.0) versioni di Bash:

printf '[%q]\n' "Ne'er do well"
# Prints [Ne\'er\ do\ well]

printf '[%q]\n' 'Sneaky injection $( whoami ) `ls /root`'
# Prints [Sneaky\ injection\ \$\(\ whoami\ \)\ \`ls\ /root\`]

Questo trucco può essere utilizzato anche per restituire matrici di dati da una funzione:

function getData()
{
  printf '%q ' "He'll say hi" 'or `whoami`' 'and then $( byebye )'
}

declare -a DATA="( $( getData ) )"
printf 'DATA: [%q]\n' "${DATA[@]}"
# Prints:
# DATA: [He\'ll\ say\ hi]
# DATA: [or\ \`whoami\`]
# DATA: [and\ then\ \$\(\ byebye\ \)]

Si noti che il printfbuilt-in Bash è diverso printfdall'utilità fornita in dotazione con la maggior parte dei sistemi operativi simili a Unix. Se, per qualche motivo, il printfcomando richiama l'utilità anziché l'integrato, puoi sempre eseguire builtin printfinvece.


Non sono sicuro di come ciò sarebbe utile se ciò che avrei bisogno di stampare fosse 'Ne'\''er do well', ecc., Ad esempio le virgolette incluse nell'output.
Walf

1
@Walf Penso che non capisci che le due forme sono equivalenti, ed entrambe sono perfettamente sicure l'una rispetto all'altra. Ad esempio [[ 'Ne'\''er do well' == Ne\'er\ do\ well ]] && echo 'equivalent!', echeggeràequivalent!
Dejay Clayton il

Mi è mancato: P preferisco il modulo citato poiché è più facile da leggere in un visualizzatore / editor che evidenzia la sintassi.
Walf

@Walf sembra che il tuo approccio sia piuttosto pericoloso, considerando che nel tuo esempio Perl, passare un valore come 'hello'risulta in un valore errato ''\''hello'', che ha una stringa vuota iniziale non necessaria (le prime due virgolette singole) e una virgoletta singola finale inappropriata.
Dejay Clayton,

1
@Walf, per chiarimenti, $'escape-these-chars'è la funzione di quotazione ANSI-C di Bash che fa sì che tutti i caratteri all'interno della stringa specificata vengano sottoposti a escape. Pertanto, per creare facilmente una stringa letterale che contiene una nuova riga all'interno del nome file (ad esempio $'first-line\nsecond-line'), utilizzare \nall'interno di questo costrutto.
Dejay Clayton

8

Immagino di non aver fatto RTFM. Si può fare così:

q_mid=\'\\\'\'
foo_esc="'${foo//\'/$q_mid}'"

Quindi echo "$foo_esc"dà l'atteso'bar'\''baz'


Il modo in cui lo sto usando è con una funzione:

function esc_var {
    local mid_q=\'\\\'\'
    printf '%s' "'${1//\'/$mid_q}'"
}

...

foo_esc="`esc_var "$foo"`"

Modificandolo per utilizzare la soluzione printfintegrata di Dejay:

function esc_vars {
    printf '%q' "$@"
}

3
Userei la soluzione di @ michael-homer se la mia versione la supportasse.
Walf,

4

Esistono diverse soluzioni per citare un valore var:

  1. alias
    Nella maggior parte delle shell (dove l'alias è disponibile) (tranne csh, tcsh e probabilmente altri come csh):

    $ alias qux=bar\'baz
    $ alias qux
    qux='bar'\''baz'

    Sì, questo funziona in molti shtipi di conchiglie come trattino o cenere.

  2. imposta
    anche nella maggior parte delle shell (di nuovo, non in csh):

    $ qux=bar\'baz
    $ set | grep '^qux='
    qux='bar'\''baz'
  3. comporre
    in alcune shell (almeno ksh, bash e zsh):

    $ qux=bar\'baz
    $ typeset -p qux
    typeset qux='bar'\''baz'             # this is zsh, quoting style may
                                         # be different for other shells.
  4. esportazione
    Prima cosa:

    export qux=bar\'baz

    Quindi utilizzare:
    export -p | grep 'qux=' export -p | grep 'qux='
    export -p qux

  5. virgolette
    echo "${qux@Q}"
    echo "${(qq)qux}" # da una a quattro q possono essere usate.


L'approccio alias è intelligente e sembra che sia specificato da POSIX. Per la massima portabilità, penso che questa sia la strada da percorrere. Credo che i suggerimenti che coinvolgono grepcon exporto setpossono rompersi su variabili contenenti ritorni a capo incorporati.
jw013,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.