tl; dr
Una suola stuffavrebbe molto probabilmente lavorare per voi.
Risposta completa
Che succede
Quando corri foo $(stuff), ecco cosa succede:
stuff piste;
- il suo output (stdout), invece di essere stampato, sostituisce
$(stuff)nell'invocazione di foo;
- quindi
fooviene eseguito, i suoi argomenti della riga di comando ovviamente dipendono da ciò che è stato stuffrestituito.
Questo $(…)meccanismo si chiama "sostituzione comando". Nel tuo caso il comando principale è echoche fondamentalmente stampa i suoi argomenti della riga di comando su stdout. Quindi qualunque cosa stufftenti di stampare su stdout viene catturato, passato echoe stampato su stdout da echo.
Se vuoi che l'output di stuffsia stampato su stdout, esegui la suola stuff.
La `…`sintassi ha lo stesso scopo di $(…)(con lo stesso nome: "sostituzione comando"), tuttavia ci sono alcune differenze, quindi non è possibile scambiarle ciecamente. Vedi queste FAQ e questa domanda .
Dovrei evitare echo $(stuff)qualunque cosa?
C'è un motivo che potresti voler usare echo $(stuff)se sai cosa stai facendo. Per lo stesso motivo dovresti evitare echo $(stuff)se non sai davvero cosa stai facendo.
Il punto è stuffe echo $(stuff)non sono esattamente equivalenti. Quest'ultimo significa chiamare l'operatore split + glob sull'output di stuffcon il valore predefinito di $IFS. La doppia citazione della sostituzione del comando impedisce questo. La virgoletta singola per la sostituzione del comando non la rende più una sostituzione di comando.
Per osservare questo quando si tratta di dividere eseguire questi comandi:
echo "a b"
echo $(echo "a b")
echo "$(echo "a b")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "a b")'
E per globbing:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
Come puoi vedere echo "$(stuff)"è equivalente (-ish *) a stuff. Potresti usarlo ma che senso ha complicare le cose in questo modo?
D'altra parte, se si desidera che l'output di stuffsubisca la suddivisione + globbing, potrebbe essere echo $(stuff)utile. Tuttavia, deve essere una tua decisione consapevole.
Ci sono comandi che generano output che dovrebbero essere valutati (che include split, globbing e altro) ed eseguiti dalla shell, quindi eval "$(stuff)"è una possibilità (vedi questa risposta ). Non ho mai visto un comando che necessita del suo output per subire ulteriori scissioni + globbing prima di essere stampato . L'uso deliberato echo $(stuff)sembra molto raro.
Che dire var=$(stuff); echo "$var"?
Buon punto. Questo frammento:
var=$(stuff)
echo "$var"
dovrebbe essere equivalente a echo "$(stuff)"equivalente (-ish *) a stuff. Se è l'intero codice, esegui stuffinvece.
Se, tuttavia, è necessario utilizzare l'output di stuffpiù di una volta, questo approccio
var=$(stuff)
foo "$var"
bar "$var"
di solito è meglio di
foo "$(stuff)"
bar "$(stuff)"
Anche se lo fooè echoe ottieni il echo "$var"tuo codice, potrebbe essere meglio mantenerlo in questo modo. Cose da considerare:
- Con
var=$(stuff) stuffcorse una volta; anche se il comando è veloce, evitare di calcolare due volte lo stesso output è la cosa giusta. O forse stuffha effetti diversi dalla scrittura su stdout (ad esempio la creazione di un file temporaneo, l'avvio di un servizio, l'avvio di una macchina virtuale, la notifica a un server remoto), quindi non si desidera eseguirlo più volte.
- Se
stuffgenera output dipendente dal tempo o in qualche modo casuale, è possibile ottenere risultati incoerenti da foo "$(stuff)"e bar "$(stuff)". Dopo che var=$(stuff)il valore di $varè stato risolto, puoi essere sicuro foo "$var"e bar "$var"ottenere un argomento della riga di comando identico.
In alcuni casi, invece di foo "$var"te, potresti voler (bisogno) usarlo foo $var, specialmente se stuffgenera più argomenti per foo(una variabile di matrice potrebbe essere migliore se la tua shell la supporta). Ancora una volta, sai cosa stai facendo. Quando si tratta echodella differenza tra echo $vare echo "$var"è uguale a tra echo $(stuff)e echo "$(stuff)".
* Equivalente ( -ish )?
Ho detto che echo "$(stuff)"è equivalente (-ish) a stuff. Esistono almeno due problemi che lo rendono non esattamente equivalente:
$(stuff)viene eseguito stuffin una subshell, quindi è meglio dire che echo "$(stuff)"è equivalente (-ish) a (stuff). I comandi che influiscono sulla shell in cui sono in esecuzione, se in una subshell, non influiscono sulla shell principale.
In questo esempio stuffè a=1; echo "$a":
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
Confronta con
a=0
a=1; echo "$a" # stuff
echo "$a"
e con
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
Un altro esempio, inizia con stuffessere cd /; pwd:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
e test stuffe (stuff)versioni.
echonon è un buon strumento per visualizzare dati non controllati . Questo di echo "$var"cui stavamo parlando avrebbe dovuto essere printf '%s\n' "$var". Ma poiché la domanda menziona echoe poiché la soluzione più probabile non è quella di utilizzare echoin primo luogo, ho deciso di non presentarmi printffino ad ora.
stuffoppure (stuff)interleave l'output stdout e stderr, mentre echo $(stuff)stampa tutto l'output stderr da stuff(che viene eseguito per primo) e solo successivamente l'output stdout digerito da echo(che viene eseguito per ultimo).
$(…)rimuove qualsiasi nuova riga finale e quindi la echoaggiunge nuovamente. Quindi echo "$(printf %s 'a')" | xxdfornisce un output diverso rispetto a printf %s 'a' | xxd.
Alcuni comandi ( lsad esempio) funzionano in modo diverso a seconda che l'output standard sia una console o meno; quindi ls | catnon fa lo stesso ls. Allo stesso modo echo $(ls)funzionerà diversamente da ls.
Mettendo lsda parte, in un caso generale se devi forzare questo altro comportamento, allora stuff | catè meglio di echo $(ls)o echo "$(ls)"perché non scatena tutte le altre questioni menzionate qui.
Eventualmente diverso stato di uscita (menzionato per completezza di questa risposta wiki; per i dettagli vedere un'altra risposta che merita credito).