Come posso disinfettare o sfuggire a percorsi assoluti restituiti da realpath o readlink?


9

realpathe readlinkrestituisce percorsi assoluti:

+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome

Un percorso del genere è facile da affrontare. Tuttavia, qualcosa del genere avrà alcuni problemi:

inserisci qui la descrizione dell'immagine

Per esempio:

inserisci qui la descrizione dell'immagine

Quindi un nome come questo deve essere disinfettato per poterlo alimentare ad altri comandi. Un caso d'uso potrebbe essere qualcosa del genere:

+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon 

Inutile dire vim $baconche non funzionerà come previsto.

Cosa posso fare per disinfettare quel percorso assoluto in modo che funzioni con altri comandi?


3
vim "$bacon"
Cita

@steeldriver Perché è quello?
Akiva

3
Perché se non lo fai, ti trovi in ​​situazioni come questa :)
terdon

Risposte:


12

Come farlo correttamente

Prima di tutto, cita sempre le tue variabili . Quello che stai cercando di fare funziona bene se lo citi correttamente:

$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon

Ho mantenuto lo strano nome del file che hai scelto ( anche se non ho idea del perché lo hai scelto ) per motivi di coerenza.

Ora, assegniamo il percorso di pullingATerdonuna variabile e quindi proviamo ad aprire il file:

$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Ciò fallisce, come previsto. Ma, se ora lo citiamo correttamente:

$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'

Funziona come previsto. E sì, puoi anche aprire il percorso in un (corretto) editor: emacs "$bacon"funzionerà bene. OK, così sarà vime qualsiasi altra cosa. La tua scelta dell'editor, sebbene sfortunata, non è rilevante.


Perché il tuo ha fallito

Un modo rapido per rintracciare ciò che è realmente accaduto nel tuo caso è quello di usarlo set -x(spegnilo di nuovo con set +x), che fa stampare alla shell ogni comando che eseguirà prima di eseguirlo. attiva i messaggi di debug della shell con set -x:

$ set -x
$ /bin/ls $bacon 
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Che ci mostra che lsè stato eseguito con tre argomenti separati: '/home/terdon/foo/\e[92mM@r|<', '+'\''|'\''|_e|\|\|0rth'e '[`-_-"]/pullingATerdon'. Ciò accade perché la shell esegue la suddivisione delle parole e l'espansione glob su stringhe non quotate. In questo caso, il problema è la divisione delle parole, poiché la shell ha visto gli spazi nel percorso e ha letto ogni stringa separata da spazio come argomento separato.

L' mkdiresempio è leggermente diverso, ma è perché ci stai mostrando il messaggio di errore dalla seconda invocazione del comando. Immagino che tu l'abbia provato una volta e poi lo abbia eseguito una seconda volta per ottenere l'output della tua domanda. La prima volta che l'hai eseguito, sarebbe stato così:

$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory

Ancora una volta, ciò proverà a creare tre directory, non una, a causa della divisione delle parole. Innanzitutto, ha creato (correttamente) la directory /home/terdon/foo/\e[92mM@r|<:

$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'

Quindi, anche con successo, ha creato una directory chiamata +'|'|_e|\|\|0rthnella directory corrente:

$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon    0 Mar 15 00:36  pullingATerdon

E poi, ha tentato di creare la directory [`-_-"]/pullingATerdon. Questo non è riuscito perché mkdir, per impostazione predefinita, non crea sottodirectory (può, se lo esegui con -p):

$ mkdir baz/bar
mkdir: cannot create directory baz/bar’: No such file or directory

Dato che la tua stringa non quotata conteneva un /, mkdirconsiderato che un percorso di due directory, ha cercato di trovare la prima e non è riuscito.

Ecco perché ha fallito, ma quello che è successo è più complicato. La stringa che hai usato è in realtà un glob di shell, in particolare una gamma glob , che corrisponde a tutti i file nella directory corrente il cui nome è uno dei 5 caratteri `, -, _o ". Dal momento che non hai tali file nella tua directory corrente, il glob non corrisponde a nulla e, come è il comportamento predefinito in bash, restituisce se stesso:

$ echo "[\`-_-\"]/pullingATerdon"  ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon'    ## but it echoes the right thing
[`-_-"]/pullingATerdon             ## and matches nothing, so returns itself.

Per chiarire, ecco cosa succede se dai un glob che corrisponde a qualcosa:

$ echo [p]*   ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*

Il non quotato [p*]viene espanso nell'elenco dei nomi di file corrispondenti (solo uno, in questo caso) e questo è ciò a cui viene passato echo. Ancora un altro motivo per cui dovresti citare tutte le cose.

Infine, l'errore effettivo che mostri è dalla seconda volta che hai eseguito il comando e non riesce al primo passaggio, quando tenti di creare /home/terdon/foo/\e[92mM@r|<, perché la precedente chiamata aveva già creato quella directory.


Più in generale, ogni volta che ti trovi a lavorare con nomi di file arbitrari, usa sempre i globs di shell. Cose come questa:

for file in *; do command "$file"; done

Funzionerà con qualsiasi nome di file. Non importa cosa capiti di contenere. Nel nostro esempio sopra, avresti potuto fare:

emacs /home/terdon/*92mM*/pullingATerdon

Qualsiasi glob che identifichi in modo univoco il file di destinazione. In questo modo, non devi preoccuparti dei personaggi speciali e puoi semplicemente lasciarli gestire dalla shell.


Alcuni riferimenti utili:

  1. Come posso trovare e gestire in modo sicuro i nomi dei file contenenti newline, spazi o entrambi? : Una delle FAQ sull'eccellente Wiki di Gray Cat.

  2. Implicazioni sulla sicurezza di dimenticare di citare una variabile nelle shell bash / POSIX : lo stesso post a cui ho fatto riferimento all'inizio di questa risposta. Una spiegazione eccellente e molto dettagliata di tutte le cose che potrebbero andare storte se non si citano correttamente le variabili della shell.

  3. Perché il mio script shell si soffoca su spazi bianchi o altri caratteri speciali? : tutto ciò che avresti sempre voluto sapere sulla gestione di nomi di file arbitrari nella shell.

  4. Quando è necessaria la doppia citazione? : Ulteriori informazioni su virgolette e variabili e, in particolare, sui pochi casi in cui non è necessario citarli

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.