web-dev-qa-db-fra.com

Comment puis-je affecter la sortie d'une commande à une variable Shell?

Je veux affecter le résultat d'une expression à une variable et le concaténer avec une chaîne, puis l'é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 donne:

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

Il semble donc que cela ne soit pas attribué à $thefile, et est en cours d'impression lors de son exécution.

82
Nathan G.

Une affectation Shell est un seul mot, sans espace après le signe égal. Donc, ce que vous avez écrit affecte une valeur vide à thefile; de plus, puisque l'affectation est groupée avec une commande, elle fait de thefile une variable d'environnement et l'affectation est locale à cette commande particulière, c'est-à-dire que seul l'appel à ls voit la valeur assignée.

Vous voulez capturer la sortie d'une commande, vous devez donc utiliser substitution de commande :

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

(Certains documents montrent une syntaxe alternative thefile=`ls …`; La syntaxe des guillemets arrière est équivalente à la syntaxe dollar-parenthèses sauf que la citation à l'intérieur des guillemets arrière est parfois bizarre, alors utilisez simplement $(…).)

Autres remarques sur votre script:

  • Combiner -t (Trier par heure) avec -U (Ne pas trier) n'a pas de sens; utilisez simplement -t.
  • Plutôt que d'utiliser grep pour faire correspondre les captures d'écran, il est plus clair de passer un caractère générique à ls et d'utiliser head pour capturer le premier fichier:

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

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

    echo "Most recent screenshot is: $thefile"
    

    Sans guillemets doubles, la valeur de la variable est à nouveau agrandie, 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 en cas d'échec d'une commande (en renvoyant un état différent de zéro).

Si vous avez GNU find (en particulier si vous utilisez Linux non intégré ou Cygwin), il existe une autre approche pour trouver le fichier le plus récent: demandez à find de répertorier les fichiers et leurs dates, et utilisez sort et tail pour extraire 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 a qualificatifs globaux qui permettent des correspondances génériques non seulement sur les noms mais aussi sur le fichier métadonnées. La partie (om[1]) Après le motif est les qualificatifs glob; om trie les correspondances en augmentant l'âge (c'est-à-dire en fonction de l'heure de modification, la plus récente en premier) et [1] extrait uniquement la première correspondance. La correspondance entière doit être entre parenthèses car il s'agit techniquement d'un tableau, car la globalisation renvoie une liste de fichiers, même si [1] 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"

Si vous voulez le faire avec une ou plusieurs commandes multilignes/multiples, 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"

Production:

first
second
third
4
Jahid