Wie kann ich die Ausgabe eines Befehls einer Shell-Variablen zuweisen?

76

Ich möchte das Ergebnis eines Ausdrucks einer Variablen zuweisen und mit einer Zeichenfolge verknüpfen und dann als Echo ausgeben. Folgendes habe ich:

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

Aber das gibt aus:

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

Es sieht also so aus, als würde das nicht zugewiesen $thefileund wird gedruckt, während es ausgeführt wird.

Nathan G.
quelle

Antworten:

108

Eine Shell-Zuweisung ist ein einzelnes Wort ohne Leerzeichen nach dem Gleichheitszeichen. Was Sie geschrieben haben, weist also einen leeren Wert zu thefile. Da die Zuweisung mit einem Befehl gruppiert ist, wird thefileeine Umgebungsvariable erstellt, und die Zuweisung ist für diesen bestimmten Befehl lokal, dh nur für den Aufruf, lsder den zugewiesenen Wert sieht.

Sie möchten die Ausgabe eines Befehls erfassen, daher müssen Sie die Befehlsersetzung verwenden :

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

(Einige Literaturstellen zeigen eine alternative Syntax thefile=`ls …`; die Backquote-Syntax entspricht der Syntax in Dollar-Klammern, mit der Ausnahme, dass das Zitieren in Backquotes manchmal seltsam ist. Verwenden Sie einfach $(…).)

Weitere Anmerkungen zu Ihrem Skript:

  • Das Kombinieren -t(nach Zeit sortieren) mit -U(nicht sortieren) ergibt keinen Sinn. benutze einfach -t.
  • Anstatt grepScreenshots abzugleichen, ist es einfacher, einen Platzhalter zu übergeben lsund headdie erste Datei zu erfassen:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
  • Es ist im Allgemeinen eine schlechte Idee, die Ausgabe von zu analysierenls . Dies kann sehr schlimm sein, wenn Sie Dateinamen mit nicht druckbaren Zeichen haben. Das Sortieren von Dateien nach Datum ist jedoch schwierig ls, und daher ist es eine akzeptable Lösung, wenn Sie wissen, dass Dateinamen keine nicht druckbaren Zeichen oder umgekehrten Schrägstriche enthalten.

  • Verwenden Sie immer doppelte Anführungszeichen um variable Substitutionen , dh schreiben Sie hier

    echo "Most recent screenshot is: $thefile"

    Ohne doppelte Anführungszeichen wird der Wert der Variablen erneut erweitert, was zu Problemen führt, wenn Leerzeichen oder andere Sonderzeichen enthalten sind.

  • Sie brauchen keine Semikolons am Ende einer Zeile. Sie sind überflüssig, aber harmlos.
  • In ein Shell-Skript ist es oft eine gute Idee, es einzuschließen set -e. Dies weist die Shell an, zu beenden, wenn ein Befehl fehlschlägt (indem ein Nicht-Null-Status zurückgegeben wird).

Wenn Sie GNU find verwenden (insbesondere wenn Sie nicht-eingebettetes Linux oder Cygwin verwenden), gibt es einen anderen Ansatz, um die neueste Datei zu finden: Sie müssen finddie Dateien und ihre Daten auflisten sortund taildie jüngste Datei mit und extrahieren.

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

Wenn Sie bereit sind, dieses Skript in zsh anstatt in bash zu schreiben, können Sie die neueste Datei auf viel einfachere Weise abrufen, da zsh Glob-Qualifikationsmerkmale enthält , die Platzhalter-Übereinstimmungen nicht nur für Namen, sondern auch für Dateimetadaten ermöglichen. Der (om[1])Teil nach dem Muster ist das Glob-Qualifikationsmerkmal. omsortiert Übereinstimmungen nach zunehmendem Alter (dh nach Änderungszeit, neueste zuerst) und [1]extrahiert nur die erste Übereinstimmung. Die gesamte Übereinstimmung muss in Klammern angegeben werden, da es sich technisch gesehen um ein Array handelt, da beim Verschieben eine Liste von Dateien zurückgegeben wird, auch wenn [1]dies in diesem speziellen Fall bedeutet, dass die Liste (höchstens) eine Datei enthält.

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"
Gilles
quelle
7
Beeindruckend! Mehr Infos, als ich mir erhofft hätte. Vielen Dank für Ihre Antwort. Ich schätze die Mühe, die Sie in diese Sache gesteckt haben, sehr! Sie haben mir gerade gezeigt, dass ich wirklich, wirklich viel zu lernen habe. Ich mache mich auf den Weg, um ein Buch über bash zu kaufen: P.
Nathan G.
3
Das würde ich als endgültige Antwort bezeichnen! :)
alex
4

Wenn Sie dies mit mehrzeiligen / mehreren Befehlen tun möchten, können Sie dies tun:

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

Oder:

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

Beispiel:

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

Ausgabe:

first
second
third
Jahid
quelle
Das ist eine schöne Antwort. Gibt es eine Möglichkeit, eine Variable als Teil des Befehls anzugeben? Zum Beispieloutput=$(echo $someVariable)
Zach Smith