Come posso assegnare l'output di un comando a una variabile di shell?


76

Voglio assegnare il risultato di un'espressione a una variabile e concatenarlo con una stringa, quindi riecheggiarlo. Ecco cosa ho:

#!/bin/bash
cd ~/Desktop;
thefile= ls -t -U | grep -m 1 "Screen Shot";
echo "Most recent screenshot is: "$thefile;

Ma questo produce:

Screen Shot 2011-07-03 at 1.55.43 PM.png
Most recent screenshot is: 

Quindi, sembra che questo non venga assegnato $thefilee venga stampato mentre viene eseguito.


Risposte:


108

Un'assegnazione di shell è una singola parola, senza spazio dopo il segno di uguale. Quindi ciò che hai scritto assegna un valore vuoto a thefile; inoltre, poiché l'assegnazione è raggruppata con un comando, crea thefileuna variabile d'ambiente e l'assegnazione è locale per quel particolare comando, ovvero solo la chiamata a lsvedere il valore assegnato.

Desideri acquisire l'output di un comando, quindi devi utilizzare la sostituzione dei comandi :

thefile=$(ls -t -U | grep -m 1 "Screen Shot")

(Alcune pubblicazioni mostrano una sintassi alternativa thefile=`ls …`; la sintassi del backquote è equivalente alla sintassi tra parentesi in dollari tranne per il fatto che la citazione all'interno di backquotes è strana a volte, quindi basta usare $(…).)

Altre osservazioni sulla tua sceneggiatura:

  • Combinare -t(ordina per tempo) con -U(non ordinare) non ha senso; basta usare -t.
  • Invece di utilizzare grepper abbinare gli screenshot, è più chiaro passare un carattere jolly lse utilizzare headper acquisire il primo file:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
  • È generalmente una cattiva idea analizzare l'output dils . Questo potrebbe non riuscire abbastanza se si hanno nomi di file con caratteri non stampabili. Tuttavia, ordinare i file per data è difficile senza ls, quindi è una soluzione accettabile se sai che non avrai caratteri non stampabili o barre rovesciate nei nomi dei file.

  • Usa sempre le virgolette doppie attorno alle sostituzioni variabili , ovvero qui scrivi

    echo "Most recent screenshot is: $thefile"

    Senza virgolette doppie, il valore della variabile viene espanso, il che causerà problemi se contiene spazi bianchi o altri caratteri speciali.

  • Non hai bisogno di punti e virgola alla fine di una riga. Sono ridondanti ma innocui.
  • In uno script di shell, è spesso una buona idea includere set -e. Questo dice alla shell di uscire se un comando fallisce (restituendo uno stato diverso da zero).

Se hai GNU find (in particolare se stai utilizzando Linux o Cygwin non embedded), c'è un altro approccio per trovare il file più recente: findelenca i file e le loro date, usa sorte tailper estrarre il file più giovane.

thefile=$(find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@ %p" |
          sort -k 1n | tail -n 1)

Se sei disposto a scrivere questo script in zsh anziché in bash, c'è un modo molto più semplice per catturare il file più recente, perché zsh ha i qualificatori glob che consentono le corrispondenze con caratteri jolly non solo sui nomi ma anche sui metadati dei file. La (om[1])parte dopo il modello sono le qualificazioni glob; omordina le partite aumentando l'età (ovvero in base al tempo di modifica, prima il più recente) ed [1]estrae solo la prima corrispondenza. L'intera corrispondenza deve essere racchiusa tra parentesi perché è tecnicamente un array, poiché il globbing restituisce un elenco di file, anche se [1]ciò significa che in questo caso particolare l'elenco contiene (al massimo) un file.

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"

7
Wow! Più informazioni di quanto avrei potuto sperare. Grazie molte volte per la tua risposta; Apprezzo davvero tutto lo sforzo che hai fatto in questo! Mi hai appena mostrato che ho davvero molto da imparare. Vado a comprare un libro su bash: P.
Nathan G.

3
Questa è quella che definirei la risposta definitiva ! :)
alex

4

Se vuoi farlo con più comandi / più comandi, puoi farlo:

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

O:

output=$(
#multiline/multiple command/s
)

Esempio:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

Produzione:

first
second
third

Questa è una bella risposta c'è un modo in cui posso specificare una variabile come parte del comando? per esempiooutput=$(echo $someVariable)
Zach Smith
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.