Comment puis-je affecter le résultat d'une commande à une variable shell?

76

Je veux affecter le résultat d'une expression à une variable et la concaténer avec une chaîne, puis l'exécuter en écho. Voici ce que j'ai

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

Mais cela produit:

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

Donc, il semble que cela ne soit pas assigné à $thefile, et soit imprimé comme il est exécuté.

Nathan G.
la source

Réponses:

108

Une assignation de shell est un mot unique, sans espace après le signe égal. Donc, ce que vous avez écrit attribue une valeur vide à thefile; de plus, l'assignation étant groupée avec une commande, elle crée thefileune variable d'environnement et l'affectation est locale pour cette commande particulière, c'est-à-dire que seul l'appel à lsvoir la valeur assignée est vu .

Vous voulez capturer le résultat d'une commande, vous devez donc utiliser la substitution de commande :

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

(Certains ouvrages proposent une syntaxe alternative thefile=`ls …`; la syntaxe backquote équivaut à la syntaxe dollar-parentheses, sauf que citer entre backquotes est parfois bizarre, utilisez-le $(…).)

Autres remarques sur votre script:

  • Combiner -t(trier par heure) avec -U(ne pas trier) n'a pas de sens; juste utiliser -t.
  • Plutôt que d'utiliser greppour faire correspondre les captures d'écran, il est plus clair de passer un caractère générique à lset headde capturer le premier fichier:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
  • C'est généralement une mauvaise idée d'analyser la sortie dels . Cela pourrait échouer assez mal si vous avez des noms de fichiers avec des caractères non imprimables. Cependant, il est difficile de trier les fichiers par date ls, c'est donc une solution acceptable si vous savez que les noms de fichier ne contiennent pas de caractères non imprimables ni de barres obliques inverses.

  • Toujours utiliser des guillemets autour des substitutions de variables , c’est-à-dire ici écrire

    echo "Most recent screenshot is: $thefile"

    Sans les guillemets doubles, la valeur de la variable est réexaminée, ce qui causera des problèmes si elle contient des espaces ou d'autres caractères spéciaux.

  • Vous n'avez pas besoin de points-virgules à la fin d'une ligne. Ils sont redondants mais inoffensifs.
  • Dans un script shell, c'est souvent une bonne idée d'inclure set -e. Cela indique au shell de quitter si une commande échoue (en renvoyant un statut différent de zéro).

Si vous avez GNU find (en particulier si vous utilisez Linux ou Cygwin non incorporé), il existe une autre approche pour rechercher le fichier le plus récent: findrépertorier les fichiers et leurs dates, puis utiliser sortet tailextraire le fichier le plus récent .

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

Si vous êtes prêt à écrire ce script dans zsh au lieu de bash, il existe un moyen beaucoup plus facile d'attraper le fichier le plus récent, car zsh dispose de qualificatifs glob qui autorisent les correspondances génériques non seulement sur les noms, mais également sur les métadonnées du fichier. La (om[1])partie après le motif correspond aux qualificatifs glob; omtrie les correspondances en fonction de l'âge (c'est-à-dire l'heure de la modification, la plus récente en premier) et [1]extrait la première correspondance uniquement. Toute la correspondance doit être entre parenthèses car c'est techniquement un tableau, car globbing renvoie une liste de fichiers, même si [1]cela signifie que dans ce cas particulier, la liste contient (au plus) un fichier.

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"
Gilles, arrête de faire le mal
la source
7
Hou la la! Plus d'informations que j'aurais pu espérer. Merci plusieurs fois pour votre réponse; J'apprécie vraiment tous les efforts que vous avez déployés! Vous venez de me montrer que j'ai vraiment beaucoup à apprendre. Je vais acheter un livre sur bash: P.
Nathan G.
3
C'est ce que j'appellerais la réponse définitive ! :)
alex
4

Si vous voulez le faire avec multiline / multiple command / s, vous pouvez le faire:

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

Ou:

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

Exemple:

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

Sortie:

first
second
third
Jahid
la source
C'est une bonne réponse. Existe-t-il un moyen de spécifier une variable dans le cadre de la commande? par exempleoutput=$(echo $someVariable)
Zach Smith le