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 pullingATerdon
una 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à vim
e 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' mkdir
esempio è 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|\|\|0rth
nella 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 /
, mkdir
considerato 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:
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.
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.
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.
Quando è necessaria la doppia citazione? : Ulteriori informazioni su virgolette e variabili e, in particolare, sui pochi casi in cui non è necessario citarli
vim "$bacon"