Variabile tra virgolette "$ ()"


12

Ho scritto questo script:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

Sono un principiante che sta cercando di imparare velocemente e ho una domanda sulle virgolette della variabile .

Metti una variabile tra $ (), la capisco, ma perché le virgolette sono necessarie anche ifnell'istruzione? È per fare un comando nidificato?


5
Nota che while [ true ]produce un ciclo infinito, ma forse non per il motivo che pensi che lo faccia; produce while [ false ] anche un ciclo infinito, perché con un singolo argomento ha [ ... ]esito positivo se tale argomento è una stringa non vuota. while truesarà effettivamente eseguire un comando di nome true(che riesce sempre).
Chepner,

4
Non inserisci una variabile all'interno $(). Hai messo un comando dentro $().
Carattere jolly

Risposte:


22

Le virgolette impediscono la "divisione delle parole". Cioè: scomporre le variabili in più elementi in spazi bianchi (o, per essere più precisi, in spazi, tabulazioni e nuove righe come definito nel valore della $IFSvariabile shell predefinita ).

Per esempio,

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

Qui definiamo la howmanyfunzione che ci fa semplicemente sapere quanti parametri posizionali sono dati. Come puoi vedere, ci sono due elementi che vengono passati alla variabile e con le virgolette il testo nella variabile viene trattato come una sola unità.

Questo è importante per il passaggio accurato di informazioni. Ad esempio, se la variabile contiene il percorso del file e il nome del file contiene spazi in qualsiasi punto del percorso, il comando che si sta tentando di eseguire potrebbe non riuscire o dare risultati imprecisi. Se stessimo provando a creare un file con la $varvariabile, touch $varcreeremmo due file, ma touch "$var"solo uno.

Lo stesso vale per la tua [ "$currentoutput" != "$lastoutput" ]parte. Questo particolare test esegue un confronto su due stringhe. Quando viene eseguito il test, il [comando dovrebbe visualizzare 3 argomenti: una stringa di testo, l' !=operatore e un'altra stringa di testo. Mantenere le virgolette doppie impedisce la divisione delle parole e il [comando visualizza esattamente questi 3 argomenti. Ora cosa succede se le variabili non sono quotate?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

Qui, si verifica la divisione delle parole e [vede invece due stringhe helloe worldseguite da !=, seguite da altre due stringhe hi world. Il punto chiave è che senza virgolette doppie, il contenuto delle variabili è inteso come unità separate anziché come un intero elemento.

L'assegnazione della sostituzione di comando non richiede virgolette doppie come in

var=$( df )

dove hai dfsalvato l'output del comando var. Tuttavia, è una buona abitudine raddoppiare sempre le variabili tra virgolette e sostituire i comandi a $(...)meno che non si desideri effettivamente che l'output sia trattato come elementi separati.


In una nota a margine, il

while [ true ]

parte può essere

while true

[è un comando che valuta i suoi argomenti ed [ whatever ]è sempre vero indipendentemente da ciò che è dentro. Al contrario, while trueutilizza il comando trueche restituisce sempre lo stato di uscita riuscita (ed è esattamente ciò di cui ha whilebisogno il ciclo). La differenza è un po 'più di chiarezza e meno test eseguiti. In alternativa, puoi anche usare :invece ditrue

Le doppie virgolette in echo "" date and Timeparte potrebbero essere probabilmente rimosse. Inseriscono semplicemente una stringa vuota e aggiungono ulteriore spazio all'output. Se lo desideri, sentiti libero di tenerli lì, ma in questo caso non c'è un valore funzionale particolare.

lsusb >> test.log

Questa parte potrebbe probabilmente essere sostituita con echo "$currentoutput" >> test.log. Non c'è motivo di correre di lsusbnuovo dopo che è già stato eseguito currentoutput=$(lsusb). Nei casi in cui le nuove righe finali devono essere preservate nell'output, si potrebbe vedere il valore nell'esecuzione di un comando più volte, ma in questo caso lsusbnon è necessario. Meno comandi esterni vengono chiamati, meglio è, perché ogni chiamata a un comando non incorporato comporta costi in CPU, utilizzo della memoria e tempo di esecuzione (anche se i comandi sono probabilmente precaricati dalla memoria).


Guarda anche:


1
Ottima risposta, se non l'hai già fatto, dovresti scrivere un libro su bash. Ma nell'ultima parte non dovrebbe essere echo "$currentoutput" >> test.log meglio printf '%s\n' "$currentoutput" >> test.log ?
pLumo

2
@RoVo Sì, printfdovrebbe essere preferito per la portabilità. Dato che stiamo usando uno script specifico per bash qui, può essere scusato per l'uso echo. Ma la tua osservazione è molto corretta
Sergiy Kolodyazhnyy il

1
@SergiyKolodyazhnyy, ... Non sono sicuro che echosia completamente affidabile anche quando la shell è nota per essere bash (e il valore dei flag xpg_echoe posixè noto); se il valore può essere -no -e, può scomparire anziché essere stampato.
Charles Duffy,

4

In currentoutput="$(lsusb)"lsusb non è una variabile, è un comando. Cosa fa questa affermazione, esegue il lsusbcomando e assegna il suo output alla currentoutputvariabile.

La sintassi precedente per questo era

currentoutput=`lsusb`

puoi trovarlo in molti esempi e script

Per rispondere all'altra parte della tua domanda, if [ ]è proprio come ifviene definita la sintassi per bash. Vedi di più in https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html


3
Penso che sia importante dire che in [ ]realtà è il testcomando. Puoi usare anche le ifistruzioni con altri comandi, poiché si basa su un test 0 o diverso da zero del loro codice di uscita.
Arronical,

... anzi, è molto meglio correre if grep -qe "somestring" fileche correre grep -qe "somestring" file; if [ $? = 0 ]; then ..., quindi l'affermazione che if [ ...fa parte della definizione di ifsintassi non è solo fuorviante ma porta a cattive pratiche.
Charles Duffy,

4

Di seguito viene eseguito il comando esterno command e restituisce il suo output.

"$(command)"

Senza parentesi / parentesi, questo cercherebbe una variabile invece di eseguire un comando:

"$variable"

Per quanto riguarda la differenza tra $variablee "$variable", questo diventa rilevante quando $variablecontiene spazi. Durante l'utilizzo "$variable", l'intero contenuto della variabile verrà inserito in una singola stringa anche se il contenuto include spazi. Quando si utilizza $variableil contenuto della variabile può essere espanso in un elenco di argomenti con più argomenti.


Ciao @thomasrutter, mi dispiace, intendevo le virgolette ... Modifico ora il mio commento!
Shankhara,

0

Per essere contrari - bash raccomanda di usare il costrutto [[ ... ]]over [ ... ]per evitare di dover citare le variabili nel test e quindi i problemi associati alla suddivisione delle parole che gli altri hanno sottolineato.

[viene fornito in bash per la compatibilità POSIX con gli script destinati a essere eseguiti #!/bin/sho quelli trasferiti su bash - per la maggior parte, dovresti evitarlo a favore [[.

per esempio

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal

bashnon formula tale raccomandazione; essa prevede [[ ... ]] che può essere più conveniente e ha alcune funzionalità che [ ... ]non lo fa, ma non v'è alcun problema con l'utilizzo [ ... ] corretto .
Chepner,

@chepner - Forse dovrei essere più chiaro qui. Certo non è una raccomandazione esplicita nella manpage di bash, ma dato che [[fa tutto ciò che [fa e di più e ancora molte volte fa [inciampare le persone e poi cerca di aggiornare le funzionalità di [[back [solo per fallire e lasciare i bug sparsi: è giusto dire che è un le raccomandazioni della comunità nella misura in cui le guide di stile bash più pertinenti non[ consiglieranno mai di usarle .
Shalomb,
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.