È pericoloso eseguire l'eco senza virgolette?


11

Ho visto un paio di argomenti simili, ma si riferiscono a non quotare le variabili, che so potrebbero portare a risultati indesiderati.

Ho visto questo codice e mi chiedevo se sarebbe possibile iniettare qualcosa da eseguire quando viene eseguita questa riga di codice:

echo run after_bundle


Mi sono imbattuto in questo quando avevo: target = "*** LIVE SERVER ***"; target echo: $ target; e il *** si espanse in un elenco di cartelle ... 😬
Matt Parkins

Risposte:


17

Per il caso specifico

echo run after_bundle

la quotazione non è necessaria. Non è necessaria la citazione perché l'argomento echoè stringhe statiche che non contengono espansioni variabili o sostituzioni di comandi, ecc. Sono "solo due parole" (e, come sottolinea Stéphane , sono inoltre costruite dal set di caratteri portatile ).

Il "pericolo" si presenta quando si gestiscono dati variabili che la shell può espandere o interpretare. In questi casi, bisogna fare attenzione che la shell faccia la cosa giusta e che il risultato sia quello che si intende.

Le seguenti due domande contengono informazioni rilevanti a riguardo:


echoviene talvolta utilizzato per "proteggere" comandi potenzialmente dannosi nelle risposte su questo sito. Ad esempio, posso mostrare come rimuovere i file o spostare i file in una nuova destinazione utilizzando

echo rm "${name##*/}.txt"

o

echo mv "$name" "/new_dir/$newname"

Ciò genererebbe comandi sul terminale invece di rimuovere o rinominare effettivamente i file. L'utente può quindi ispezionare i comandi, decidere che sembrano ok, rimuoverlo echoed eseguirlo di nuovo.

Il tuo comando echo run after_bundlepuò essere un'istruzione per l'utente o può essere un pezzo di codice "commentato" che è troppo pericoloso per essere eseguito senza conoscerne le conseguenze.

Usando in echoquesto modo, si deve sapere cosa fa il comando modificato e si deve garantire che il comando modificato sia effettivamente sicuro (potenzialmente non lo sarebbe se contenesse reindirizzamenti, e usarlo su una pipeline non funziona, ecc.)


L'aggiunta di virgolette non è sufficiente per sapere cosa farebbe una shell, proprio come non si può dire che echo rm "first file.txt" "second file.txt"sia in qualche modo diverso da echo rm "first" "file.txt" "second" "file.txt", l'output di entrambi è lo stesso. Se si desidera generare un comando shell come output, è necessario utilizzare printf '%q ' rm "first file.txt" "second file.txt"; echoo qualcosa di equivalente che rigeneri la quotazione sintattica che valuti il argvpassato.
Charles Duffy,

@CharlesDuffy Spero davvero che nessuno copi-incolla l'output di debug e lo esegua nella shell!
Kusalananda

1
Generare comandi shell e poi reindirizzarli shnon è esattamente uno schema insolito, e vedere la gente chiedere "perché foofunziona quando lo eseguo su una riga di comando, ma questo script che emette quella stringa esatta con echodavanti alla riga no? " succede sempre qui. Più precisamente, l'output di debug non è utile se nasconde i tuoi bug - e se i tuoi bug sono correlati alla citazione, echonon li riveleranno.
Charles Duffy,

27

Solo una nota in aggiunta alla bella risposta di @ Kusalananda .

echo run after_bundle

va bene perché nessuno dei caratteri in questi 3 argomenti¹ è passato a echocontenere caratteri speciali per la shell.

E (il punto in più che voglio sottolineare qui) non esiste alcuna locale di sistema in cui quei byte possano essere tradotti in caratteri speciali della shell.

Tutti quei personaggi sono in ciò che POSIX chiama il set di caratteri portatile . Questi caratteri dovrebbero essere presenti e codificati allo stesso modo in tutti i set di caratteri su un sistema POSIX².

In tal modo la riga di comando verrà interpretata allo stesso modo indipendentemente dalla locale.

Ora, se iniziamo a usare caratteri al di fuori di quel set di caratteri portatile, è una buona idea citarli anche se non sono speciali per la shell, perché in un'altra locale, i byte che li costituiscono possono essere interpretati come caratteri diversi che potrebbero diventare speciale per la shell. Nota che è se stai usando echoo qualsiasi altro comando, il problema non è con echoma con come la shell analizza il suo codice.

Ad esempio in un UTF-8:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

Questo àè codificato come 0xc3 0xa0. Ora, se hai quella riga di codice in uno script di shell e lo script di shell viene invocato da un utente che utilizza una locale il cui set di caratteri non è UTF-8, quei due byte potrebbero creare caratteri molto diversi.

Ad esempio, in una fr_FR.ISO8859-15locale, una tipica locale francese che utilizza il set di caratteri a byte singolo standard che copre la lingua francese (la stessa utilizzata per la maggior parte delle lingue dell'Europa occidentale incluso l'inglese), che 0xc3 byte viene interpretato come Ãcarattere e 0xa0 come non- carattere spazio di rottura.

E su alcuni sistemi come NetBSD³, quello spazio non-break viene considerato come un carattere vuoto ( isblank()su esso ritorna vero, è abbinato da [[:blank:]]) e shell come bashquindi lo trattano come un delimitatore di token nella loro sintassi.

Ciò significa che invece di funzionare echocon $'voil\xc3\xa0'come argomento, lo eseguono $'voil\xc3'come argomento, il che significa che non verrà stampato voilàcorrettamente.

Si ottiene molto peggio con i set di caratteri cinesi come BIG5, BIG5-HKSCS, GB18030, GBK che hanno molti personaggi le cui codifica contiene la stessa codifica come |, `, \(per citare il peggiore) (anche che SJIS ridicola, aka Microsoft Kanji, ad eccezione che è ¥invece di \, ma è ancora trattato \dalla maggior parte degli strumenti in quanto è codificato come 0x5c lì).

Ad esempio, se in una lingua zh_CN.gb18030cinese, scrivi uno script come:

echo  reboot

Lo script verrà generato 詜 rebootin una locale usando GB18030 o GBK, 唰 rebootin una locale usando BIG5 o BIG5-HKSCS, ma in una locale C usando ASCII o una locale usando ISO8859-15 o UTF-8, verrà rebooteseguito perché la codifica GB18030 di è 0xd4 0x7c e 0x7c è la codifica di |in ASCII quindi finiamo per eseguire:

 echo �| reboot

(che rappresenta comunque il byte 0xd4 è reso nella locale). Esempio usando il meno dannoso unameinvece di reboot:

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

unamestato eseguito).

Quindi il mio consiglio sarebbe di citare tutte le stringhe che contengono caratteri al di fuori del set di caratteri portatile.

Tuttavia notare che, poiché la codifica dei \e `si trovano nella codifica di alcuni di questi personaggi, non è meglio usare \o "..."o $'...'(al cui interno `e / o \sono ancora speciale), ma il '...'posto per citare personaggi al di fuori del set di caratteri portatile.

Non sono a conoscenza di alcun sistema che abbia una localizzazione in cui il set di caratteri ha un carattere (diverso da 'se stesso ovviamente) la cui codifica contiene la codifica di ', quindi quelli '...'dovrebbero sicuramente essere i più sicuri.

Notare che diverse shell supportano anche una $'\uXXXX'notazione per esprimere caratteri basati sul loro punto di codice Unicode. In shell come zshe bash, il carattere viene inserito codificato nel set di caratteri della locale (anche se può causare comportamenti imprevisti se quel set di caratteri non ha quel carattere). Ciò consente di evitare di inserire caratteri non ASCII nel codice della shell.

Quindi sopra:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

O:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(con l'avvertenza che potrebbe rompere la sceneggiatura quando eseguito in locali che non hanno quei caratteri).

O meglio, poiché \è anche speciale per echo(o almeno alcune echo implementazioni, almeno quelle conformi a Unix):

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

(nota che \è speciale anche nel primo argomento di printf, quindi è meglio evitare anche caratteri non ASCII nel caso in cui possano contenere la codifica di \).

Nota che potresti anche fare:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(sarebbe eccessivo ma potrebbe darti un po 'di tranquillità se non sei sicuro di quali personaggi siano nel set portatile)

Assicurati anche di non usare mai l'antica `...`forma di sostituzione dei comandi (che introduce un altro livello di elaborazione della barra rovesciata), ma usa $(...)invece.


¹ tecnicamente, echoviene anche passato come argomento echoall'utilità (per dirlo come è stato invocato), è il argv[0]e argcè 3, anche se nella maggior parte delle shell al giorno d'oggi echoè incorporato, quindi quello exec()di un /bin/echofile con un elenco di 3 argomenti è simulato dal conchiglia. È anche comune considerare l'elenco di argomenti come a partire dal secondo ( argv[1]a argv[argc - 1]) in quanto sono quelli sui quali agiscono principalmente i comandi.

² una notevole eccezione a quella che è la ridicola ja_JP.SJISlocalizzazione dei sistemi FreeBSD il cui set di caratteri non ha \~carattere!

³ nota che mentre molti sistemi (FreeBSD, Solaris, non quelli GNU) considerano U + 00A0 come [[:blank:]]in locali UTF-8, pochi lo fanno in altri locali come quelli che usano ISO8859-15, forse per evitare questo tipo di problema.


Nel tuo primo paragrafo, ci dici "... dei personaggi in quei 3 argomenti passati a echo...", conto solo 2 argomenti passati al comando echo, gli argomenti che posso contare sono rune after_bundle, mi preoccupo di spiegare come tu contato e ottenuto 3 argomenti?
Ferrybig

1
@ViktorFonic, vedi modifica sul numero di argomenti (e che il problema principale non è con echo). Vedi (exec -a foo /bin/echo --help)su un sistema GNU e con la shell GNU per come passare un primo argomento arbitrario /bin/echoall'utilità.
Stéphane Chazelas,

@Ferrybig Vedi la modifica di Stephane, nota 1. Gli argomenti da comandare nel solito stile C sono una serie di argomenti, con argv [0] come nome eseguibile stesso. Un po 'simili ai $0parametri posizionali nelle shell.
Sergiy Kolodyazhnyy

Ci sono 373 codifiche iconvin cui ESCviene convertito '. Prova (ad esempio):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
NotAnUnixNazi

Esistono 173 codifiche in cui alcuni punti di codice (diversi da ESC) vengono convertiti in a '. Prova printf '\u2804' | iconv -f utf8 -t BRF | xxd. Ci sono codifiche in cui ci sono molti punti di codice che diventano '. Diventano circa 8695 punti di codice in UCS-4 '. Prova printf '\U627' | iconv -cf utf-8 -t UCS-4. Numerose (37) codifiche convertono il carattere 0x127 in a '. Tryprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd
NotAnUnixNazi
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.