Quali caratteri devono essere salvati negli argomenti della riga di comando?


14

In Bash, quando si specificano gli argomenti della riga di comando per un comando, quali caratteri devono essere sottoposti a escape?

Sono limitato ai metacaratteri di Bash: spazio, tab, |, &, ;, (, ), <, e >?


Non dimenticare (possibile) il nome del file che lampeggia con * e?
Jeff Schaller

Grazie. Potresti elencare esaurientemente i tipi di caratteri che devono essere sfuggiti in args line cmd?
Tim

L'elenco è buono da avere, ma la cosa più importante da capire sull'offerta è: Tutto tra virgolette singole viene passato letteralmente e senza divisione delle parole. Nessuna eccezione. (Ciò significa che non c'è alcun modo per incorporare una singola citazione tra virgolette singole, comunque, ma è facile aggirare .)
Wildcard

Risposte:


22

I seguenti caratteri hanno un significato speciale per la shell stessa in alcuni contesti e potrebbe essere necessario sfuggire agli argomenti:

Alcuni di questi personaggi sono usati per più cose e in più posti di quello che ho collegato.


Esistono alcuni casi angolari esplicitamente facoltativi:

  • !può essere disabilitato con set +H, che è l'impostazione predefinita nelle shell non interattive.
  • {può essere disabilitato con set +B.
  • *e ?può essere disabilitato con set -foset -o noglob .
  • =Anche il segno di uguale (U + 003D) deve essere evitato se set -koset -o keyword è abilitato.

Per uscire da una nuova riga è necessario un preventivo : le barre rovesciate non funzionano. Qualsiasi altro personaggio elencato in IFS avrà bisogno di una gestione simile. Non è necessario per fuggire ]o }, ma non ha bisogno di fuggire )perché è un operatore.

Alcuni di questi personaggi hanno limiti più severi quando hanno davvero bisogno di fuggire rispetto ad altri. Ad esempio, a#bva bene, ma a #bè un commento, mentre >dovrebbe essere evaso in entrambi i contesti. Non fa male sfuggirle tutte in modo conservativo, ed è più facile che ricordare le sottili distinzioni.

Se il nome del comando è di per sé una parola chiave shell ( if, for, do) allora avrete bisogno di fuggire o citare troppo. L'unico interessante di questi è in, perché non è ovvio che è sempre una parola chiave. Non è necessario farlo per le parole chiave utilizzate negli argomenti, solo quando hai (stupidamente!) Nominato un comando dopo uno di essi. Gli operatori Shell ( (, &ecc.) Devono sempre quotare ovunque si trovino.


1 Stéphane ha notato che anche qualsiasi altro carattere vuoto a byte singolo della tua locale deve essere evaso. Nei locali più comuni e sensibili, almeno quelli basati su C o UTF-8, sono solo i caratteri bianchi sopra. In alcuni locali ISO-8859-1, lo spazio no-break U + 00A0 è considerato vuoto, inclusi Solaris, i BSD e OS X (penso in modo errato). Se hai a che fare con un locale sconosciuto arbitrario, potrebbe includere qualsiasi cosa, comprese le lettere, quindi buona fortuna.

Concepibilmente, un singolo byte considerato vuoto potrebbe apparire all'interno di un carattere multi-byte che non era vuoto e non avresti modo di sfuggire a questo se non di mettere il tutto tra virgolette. Questa non è una preoccupazione teorica: in una locale ISO-8859-1 dall'alto, quel A0byte che è considerato uno spazio vuoto può apparire all'interno di caratteri multibyte come UTF-8 codificato "à" ( C3 A0). Per gestire quei personaggi in modo sicuro dovresti citarli "à". Questo comportamento dipende dalla configurazione locale nell'ambiente che esegue lo script, non da quello in cui è stato scritto.

Penso che questo comportamento sia rotto in molti modi, ma dobbiamo giocare la mano che ci viene data. Se stai lavorando con un set di caratteri multibyte non auto-sincronizzante, la cosa più sicura sarebbe quella di citare tutto. Se sei in UTF-8 o C, sei al sicuro (per il momento).


Anche altri spazi vuoti nel tuo locale dovrebbero essere eliminati ( tranne attualmente quello multibyte a causa di un bug )
Stéphane Chazelas

Devi solo scappare !quando è abilitata l'espansione della cronologia csh, in genere non negli script. [ ! -f a ]o find . ! -name...stanno bene. Questo è coperto dalla sezione dei limiti più stretti, ma forse vale la pena menzionarlo esplicitamente.
Stéphane Chazelas,

Nota che ci sono contesti in cui altri personaggi devono citando come: hash[foo"]"]=, ${var-foo"}"}, [[ "!" = b ]], [[ a = "]]" ]], gli operatori regexp per [[ x =~ ".+[" ]]. Altre parole chiave di {( if, while, for...) avrebbe bisogno di essere citato in modo che non stanno riconosciuti come tali ...
Stéphane Chazelas

Nella misura in cui questi sono argomenti della riga di comando, l'interpretazione dipende dal comando in questione (proprio come ]), quindi non li sto elencando. Non credo che nessuna parola chiave debba essere citata nella posizione dell'argomento.
Michael Homer,

2
La citazione di built-in, trattini o% non fa nulla.
Michael Homer,

3

In GNU Parallel questo è testato e ampiamente utilizzato:

$a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go;
# quote newline as '\n'                                                                                                         
$a =~ s/[\n]/'\n'/go;

E 'testato in bash, dash, ash, ksh, zsh, e fish. Alcuni caratteri non devono essere citati in alcune (versioni) delle shell, ma quanto sopra funziona in tutte le shell testate.

Se vuoi semplicemente citare una stringa, puoi reindirizzarla in parallel --shellquote:

printf "&*\t*!" | parallel --shellquote

Come non ho mai sentito parlare di parallelo prima ...
Tom H,

@TomH Sarà apprezzato se riesci a passare 5 minuti a pensare a come avremmo potuto raggiungerti.
Ole Tange,

Penso che sia un problema di progressione. la maggior parte delle persone non ha bisogno o capisce il parallelo fino a quando non ha progredito attraverso alcune fasi di complessità. A quel punto si sono imbattuti in xargs, nohup e cose del genere. Inoltre non vedo molte persone che usano il parallelo per risolvere i problemi nello scambio di stack o quando cerco google per soluzioni ai problemi di bash
Tom H,

1

Per la soluzione di fuga leggera in Perl, sto seguendo il principio delle virgolette singole. Una stringa di Bash tra virgolette singole può avere qualsiasi carattere, tranne la virgoletta singola stessa.

Il mio codice:

my $bash_reserved_characters_re = qr([ !"#$&'()*;<>?\[\\`{|~\t\n]);

while(<>) {
    if (/$bash_reserved_characters_re/) {
        my $quoted = s/'/'"'"'/gr;
        print "'$quoted'";
    } else {
        print $_;
    }
}

Esempio di esecuzione 1:

$ echo -n "abc" | perl escape_bash_special_chars.pl
abc

Esempio di esecuzione 2:

echo "abc" | perl escape_bash_special_chars.pl
'abc
'

Esempio di esecuzione 3:

echo -n 'ab^c' | perl escape_bash_special_chars.pl
ab^c

Esempio di esecuzione 4:

echo -n 'ab~c' | perl escape_bash_special_chars.pl
'ab~c'

Esempio di esecuzione 5:

echo -n "ab'c" | perl escape_bash_special_chars.pl
'ab'"'"'c'

echo 'ab'"'"'c'
ab'c

Sì, punto valido che. La mia opinione è che la maggior parte delle persone atterrerà su questa pagina, perché hanno un problema da risolvere. Non perché questo dia un interessante dibattito accademico. Ecco perché mi piacerebbe offrire soluzioni e discutere i loro meriti, anche se leggermente fuori tema.
Jari Turkia,

Il mio codice è solo un'implementazione della risposta di Michael Homer. Non avevo intenzione di fornire ulteriori informazioni rispetto a quello che faceva.
Jari Turkia,
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.