Lo standard POSIX impone l'espansione delle parole nel seguente ordine (enfatizzare è mio):
L'espansione della tilde (vedi espansione della tilde), l' espansione dei parametri (vedi espansione dei parametri), la sostituzione dei comandi (vedi sostituzione dei comandi) e l'espansione aritmetica (vedi l'espansione aritmetica) devono essere eseguite dall'inizio alla fine. Vedi l'articolo 5 in Riconoscimento token.
La suddivisione dei campi (vedere Divisione dei campi) deve essere eseguita sulle porzioni dei campi generate dal passaggio 1, a meno che IFS sia nullo.
L'espansione del nome del percorso (vedere Espansione del nome del percorso) deve essere eseguita, a meno che non sia attivo set -f.
La rimozione del preventivo (vedere Rimozione del preventivo) deve sempre essere eseguita per ultima.
L'unico punto che ci interessa qui è il primo: come puoi vedere l'espansione della tilde viene elaborata prima dell'espansione dei parametri:
- La shell tenta di espandere una tilde
echo $x
, non è possibile trovare tilde, quindi procede.
- La shell tenta di espandere un parametro
echo $x
, $x
viene trovata ed espansa e la riga di comando diventa echo ~/someDirectory
.
- L'elaborazione continua, poiché l'espansione della tilde è già stata elaborata, il
~
personaggio rimane così com'è.
Usando le virgolette durante l'assegnazione di $x
, si chiedeva esplicitamente di non espandere la tilde e trattarla come un normale personaggio. Una cosa che spesso manca è che nei comandi della shell non è necessario citare l'intera stringa, quindi è possibile fare l'espansione proprio durante l'assegnazione della variabile:
user@host:~$ set -o xtrace
user@host:~$ x=~/'someDirectory'
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
E puoi anche fare in modo che l'espansione avvenga sulla echo
riga di comando fintanto che può accadere prima dell'espansione dei parametri:
user@host:~$ x='someDirectory'
+ x=someDirectory
user@host:~$ echo ~/$x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Se per qualche motivo hai davvero bisogno di influenzare la tilde sulla $x
variabile senza espansione ed essere in grado di espanderla al echo
comando, devi procedere in due volte per forzare due espansioni della $x
variabile:
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ echo "$( eval echo $x )"
++ eval echo '~/someDirectory'
+++ echo /home/user/someDirectory
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Tuttavia, tenere presente che, a seconda del contesto in cui si utilizza tale struttura, potrebbe avere effetti collaterali indesiderati. Come regola generale, preferisci evitare di usare qualsiasi cosa richieda eval
quando hai un altro modo.
Se si desidera affrontare in modo specifico il problema della tilde rispetto a qualsiasi altro tipo di espansione, tale struttura sarebbe più sicura e portatile:
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ case "$x" in "~/"*)
> x="${HOME}/${x#"~/"}"
> esac
+ case "$x" in
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
Questa struttura controlla esplicitamente la presenza di un lead ~
e lo sostituisce con la home directory dell'utente se viene trovata.
A seguito del tuo commento, x="${HOME}/${x#"~/"}"
potrebbe essere davvero sorprendente per qualcuno che non viene utilizzato nella programmazione della shell, ma è in effetti collegato alla stessa regola POSIX che ho citato sopra.
Come imposto dallo standard POSIX, la rimozione del preventivo avviene per ultima e l'espansione dei parametri avviene molto presto. Pertanto, ${#"~"}
viene valutato e ampliato molto prima della valutazione delle virgolette esterne. A sua volta, come definito nelle Regole di espansione dei parametri :
In ogni caso in cui sia necessario un valore di parola (basato sullo stato del parametro, come descritto di seguito), la parola deve essere sottoposta a espansione tilde, espansione parametro, sostituzione comando ed espansione aritmetica.
Pertanto, il lato destro #
dell'operatore deve essere opportunamente citato o sfuggito per evitare l'espansione della tilde.
Quindi, per dirlo diversamente, quando l'interprete della shell guarda x="${HOME}/${x#"~/"}"
, vede:
${HOME}
e ${x#"~/"}
deve essere espanso.
${HOME}
viene espanso al contenuto della $HOME
variabile.
${x#"~/"}
innesca un'espansione nidificata: "~/"
viene analizzata ma, essendo citata, viene trattata come 1 letterale . Avresti potuto usare virgolette singole qui con lo stesso risultato.
${x#"~/"}
l'espressione stessa viene ora espansa, determinando la ~/
rimozione del prefisso dal valore di $x
.
- Il risultato di quanto sopra è ora concatenato: l'espansione
${HOME}
, letterale /
, l'espansione ${x#"~/"}
.
- Il risultato finale è racchiuso tra virgolette doppie, impedendo funzionalmente la divisione delle parole. Dico funzionalmente qui perché queste doppie virgolette non sono tecnicamente richieste (vedi qua e là per esempio), ma come stile personale non appena un compito ottiene qualcosa oltre, di
a=$b
solito trovo più chiaro aggiungere doppie virgolette.
A proposito, se osservi più da vicino la case
sintassi, vedrai la "~/"*
costruzione che si basa sullo stesso concetto che x=~/'someDirectory'
ho spiegato sopra (anche in questo caso, le virgolette doppie e semplici potrebbero essere usate in modo intercambiabile).
Non preoccuparti se queste cose possono sembrare oscure a prima vista (forse anche alla seconda o più tardi!). A mio avviso, l'espansione dei parametri è, con subshells, uno dei concetti più complessi da comprendere durante la programmazione in linguaggio shell.
So che alcune persone potrebbero essere in forte disaccordo, ma se desideri approfondire la programmazione della shell ti incoraggio a leggere la Guida avanzata agli script di Bash : insegna lo script di Bash, quindi con molte estensioni e campane e fischietti rispetto allo script di shell POSIX, ma l'ho trovato ben scritto con un sacco di esempi pratici. Una volta gestito questo, è facile limitarsi alle funzionalità POSIX quando è necessario, personalmente penso che entrare direttamente nel regno POSIX sia una curva di apprendimento ripida non necessaria per i principianti (confronta la mia sostituzione della tilde POSIX con Bash simile a regex di @ m0dular equivale a farsi un'idea di cosa intendo;)!).
1 : Il che mi porta a trovare un bug in Dash che non implementa correttamente l'espansione tilde (usando verificabile x='~/foo'; echo "${x#~/}"
). L'espansione dei parametri è un campo complesso sia per l'utente che per gli sviluppatori stessi della shell!
x='~'; print -l ${x} ${~x}
. Mi sono arreso dopo aver scavato nelbash
manuale per un po '.