Come posso espandere una variabile tra virgolette a nulla se è vuota?


21

Di 'che sto facendo uno script:

some-command "$var1" "$var2" ...

E, nel caso in cui var1sia vuoto, preferirei che venisse sostituito con nulla invece della stringa vuota, in modo che il comando eseguito sia:

some-command "$var2" ...

e non:

some-command '' "$var2" ...

Esiste un modo più semplice che testare la variabile e includerla in modo condizionale?

if [ -n "$1" ]; then
    some-command "$var1" "$var2" ...
    # or some variant using arrays to build the command
    # args+=("$var1")
else
    some-command "$var2" ...
fi

C'è una sostituzione dei parametri che può espandersi a nulla in bash, zsh o simili? Potrei ancora voler usare il globbing nel resto degli argomenti, quindi disabilitare e annullare la citazione della variabile non è un'opzione.


Sapevo di averlo visto e probabilmente l'ho usato prima, ma è stato difficile cercarlo. Ora che Michael ha mostrato la sintassi, mi sono ricordato dove l'avevo visto per la prima volta abbastanza rapidamente: unix.stackexchange.com/a/269549/70524 , unix.stackexchange.com/q/68484/70524
muru,

Se sai che è una sorta di sostituzione dei parametri, perché non hai guardato nella sezione di espansione dei parametri della manpagina? (-;
Philippos,

1
@Philippos Non sapevo cosa fosse all'epoca, solo che l'avevo visto o usato prima. Conosciuti noti e sconosciuti. :(
muru,

1
punti cookie extra per menzionare l'utilizzo di un array per contenere gli argomenti nella domanda stessa.
Ilkkachu,

Risposte:


29

Le shell compatibili con Posix e Bash hanno ${parameter:+word} :

Se il parametro non è impostato o è nullo, è necessario sostituire null; in caso contrario, verrà sostituita l'espansione della parola (o una stringa vuota se la parola viene omessa).

Quindi puoi semplicemente fare:

${var1:+"$var1"}

e devono var1essere controllati e "$var1"utilizzati se è impostato e non vuoto (con le normali regole di virgolette doppie). Altrimenti non si espande nel nulla. Si noti che qui viene citata solo la parte interna , non l'intera cosa.

Lo stesso funziona anche in zsh. Devi ripetere la variabile, quindi non è l'ideale, ma funziona esattamente come volevi.

Se si desidera che una variabile set-but-empty si espanda in un argomento vuoto, utilizzare ${var1+"$var1"}invece.


1
Abbastanza buono per me Quindi, in questo, il tutto non è citato, solo la wordparte lo è.
Muru,

1
Mentre la domanda chiedeva "bash, zsh o simili" (e per l'archivio Domande e risposte), ho modificato per riflettere che si tratta di una funzione posix. Anche se aggiungi un tag / bash alla domanda dopo il mio commento. (-;
Philippos,

2
Mi sono preso la libertà di modificare la differenza tra :+e +.
Ilkkachu,

Grazie mille per una soluzione conforme a POSIX! Cerco di usare sh/ dashper potenziali script chokepoint, quindi lo apprezzo sempre quando qualcuno mostra come una cosa è possibile senza ricorrere a Bash.
JamesTheAwesomeDude

Stavo cercando l'effetto opposto (espandi a qualcos'altro se inizia come nullo). Si scopre che questa logica può essere invertita cambiando + in a - come in: $ {empty_var: -replacement}
Alex Jansen il

5

Questo è ciò che zshfa di default quando si omettono le virgolette:

some-command $var1 $var2

In realtà, l'unico motivo per cui hai ancora bisogno di virgolette in zsh sull'espansione dei parametri è quello di evitare quel comportamento (la rimozione vuota) poiché zshnon ci sono altri problemi che influenzano altre shell quando non citate le espansioni dei parametri (la divisione implicita + glob) .

Puoi fare lo stesso con altre shell simili a POSIX se disabiliti split e glob:

(IFS=; set -o noglob; some-command $var1 $var2)

Ora, direi che se la tua variabile può avere un valore 0 o 1, dovrebbe essere un array e non una variabile scalare e usare:

some-command "${var1[@]}" "${var2[@]}"

E usa var1=(value)quando var1deve contenere un valore, var1=('')quando deve contenere un valore vuoto e var1=()quando non deve contenere alcun valore.


0

Mi sono imbattuto in questo usando rsync in uno script bash che ha avviato il comando con o senza un passaggio -na secco. Si scopre che rsync e un certo numero di comandi gnu prendono ''come primo argomento valido e agiscono in modo diverso rispetto a se non ci fosse.

Il debug ha richiesto del tempo perché i parametri null sono quasi completamente invisibili.

Qualcuno nella lista di rsync mi ha mostrato un modo per evitare questo problema, semplificando anche notevolmente la mia codifica. Se lo capisco correttamente, questa è una variazione sull'ultimo suggerimento di @ Stéphane Chazelas.

Costruisci i tuoi argomenti di comando in un numero di variabili separate. Questi possono essere impostati in qualsiasi ordine o logica adatta al problema.

Quindi, alla fine, usa le variabili per costruire un array con tutto al suo posto e usalo come argomento per il comando effettivo.

In questo modo il comando viene emesso solo in un punto del codice invece di essere ripetuto per ogni variazione degli argomenti.

Qualsiasi variabile vuota scompare semplicemente usando questo metodo.

So che usare eval è fortemente disapprovato. Non ricordo tutti i dettagli, ma mi sembrava di averne bisogno per far funzionare le cose in questo modo - qualcosa a che fare con la gestione dei parametri con lo spazio bianco incorporato.

Esempio:

dry_run=''
if [[ it is a test run ]]
then
  dry_run='-n'
fi
...
rsync_options=(
  ${dry_run}
  -avushi
  ${delete}
  ${excludes}
  --stats
  --progress
)
...
eval rsync "${rsync_options[@]}" ...

È un modo peggiore di fare ciò che ho descritto nella domanda (costruendo una serie di argomenti in modo condizionale). Lasciando le variabili non quotate, chissà a quali problemi ti stai lasciando aperto.
Muru,

@muru Hai ragione, ma ho ancora bisogno di qualcosa del genere. Non vedo come risolverlo usando le tecniche delle altre risposte. Sarebbe bello vedere un frammento di codice fatto nel modo giusto che lo mette insieme. Giocherò con l'ultima opzione di Stephane in seguito, perché potrebbe fare quello che voglio.
Joe,


Sono appena tornato a questo. Grazie per l'esempio Ha senso. Ci lavorerò.
Joe

Ho un caso d'uso simile qui, ma potresti fare qualcosa del genere: if ["$ dry" == "true"]; quindi dry = "- n"; fi ... quindi se la variabile dry è vera, la fai diventare -n, e poi costruisci il tuo comando rsync come al solito e lo fai così: rsync $ {dry1: + "$ dry1"} ... se è null non succederà nulla, altrimenti diventerà -n al tuo comando
Freedo il
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.