Das Echo eines Tail-Befehls erzeugt eine unerwartete Ausgabe?

8

Dieser Befehl erzeugt, wenn er alleine ausgeführt wird, das erwartete Ergebnis (die letzte Zeile der Crontab):

tail -n 1 /etc/crontab

Wenn ich es jedoch als Teil eines Echo-Befehls ausführe, um das Ergebnis an eine Datei zu senden, wird eine Zusammenfassung aller Dateien im Arbeitsverzeichnis sowie das erwartete Ergebnis hinzugefügt:

sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'

Warum hat dieser Befehl die zusätzlichen Daten erzeugt?

Davidw
quelle
3
Was echomacht das hier für dich? Bedenken Sie auchtail -n 1 /etc/crontab | sudo tee /path/to/file >/dev/null
Strg-Alt-Delor
1
Was ist los mit echo $(stuff)?
Kamil Maciorowski

Antworten:

22

Ihre Crontab-Linie enthält ein oder mehrere Sternchen *, die "jederzeit" anzeigen. Wenn diese Zeile durch die Befehlsersetzung ersetzt wird, ist das Ergebnis ungefähr so

echo * * * * * cmd > /path/to/file

Während die meisten weiteren Erweiterungen nicht auf die Ausgabe der Befehlssubstitution angewendet werden, lautet die Pfadnamenerweiterung (ebenso wie die Feldaufteilung) :

Die Ergebnisse der Befehlssubstitution dürfen nicht für eine weitere Tildeerweiterung, Parametererweiterung, Befehlssubstitution oder arithmetische Erweiterung verarbeitet werden. Wenn eine Befehlssubstitution in doppelten Anführungszeichen erfolgt, dürfen die Ergebnisse der Substitution nicht durch Feldaufteilung und Pfadnamenerweiterung ersetzt werden.

Die Pfadnamenerweiterung wird *.txtzu einer Liste übereinstimmender Dateinamen (Globbing), in der *alles übereinstimmt. Das Endergebnis ist, dass Sie jeden (nicht versteckten) Dateinamen im Arbeitsverzeichnis erhalten, der für jeden *in Ihrer Crontab-Zeile aufgeführt ist.


Sie können dies beheben, indem Sie die Erweiterung zitieren, wenn der von Ihnen veröffentlichte Code für einen komplexeren Befehl repräsentativ ist:

sudo bash -c 'echo "$(tail -n 1 /etc/crontab)" > /path/to/file'

aber einfacher verlieren Sie einfach das echoganz:

sudo bash -c 'tail -n 1 /etc/crontab > /path/to/file'

Dies sollte tun, was Sie wollen, und es ist auch einfacher (der einzige andere wesentliche Unterschied besteht darin, dass in dieser Version die Feldaufteilung weggelassen wird, die sonst aufgetreten wäre, sodass Leerzeichen nicht reduziert werden).

Michael Homer
quelle
5
Da / etc / crontab fast immer weltweit lesbar ist, ist der ganze Trick "bash -c" eigentlich unnötig: tail -n -1 /etc/crontab | sudo tee /path/to/fileist die Redewendung, die ich als am wenigsten fehleranfällig empfunden habe, wenn ich Ausgaben in Dateien umleitete, die Superuser-Berechtigungen erfordern.
Bass
5

Betrachten wir ein Verzeichnis mit diesen Dateien:

$ ls
crontab  file1  file2  file3
$ cat crontab
f*

Lassen Sie uns nun den Befehl tail ausführen:

$ tail -n 1 crontab
f*

Das Obige ist die letzte Zeile von crontabund das ist, was wir erwarten. Jedoch:

$ echo $(tail -n 1 crontab)
file1 file2 file3

Doppelte Anführungszeichen beseitigen dieses Problem:

$ echo "$(tail -n 1 crontab)"
f*

Ohne die doppelten Anführungszeichen wird das Ergebnis der Befehlsersetzung durch die Shell erweitert. Eine der Erweiterungen ist die Pfadnamenerweiterung . Im obigen Fall bedeutet dies, dass f*es so erweitert wird, dass es mit jedem Dateinamen übereinstimmt, der mit beginnt f.

Wenn Sie nicht ausdrücklich Shell-Erweiterungen wünschen, setzen Sie alle Shell-Variablen und / oder Befehlsersetzungen in doppelte Anführungszeichen.

John1024
quelle
4

Der Globing-Shell-Mechanismus wird *auf die lokale Datei erweitert.

crontab line hat wahrscheinlich einen *Platzhalter für jeden.

zB diese Linie in Crontab läuft am Sonntag um 7.47 Uhr, erster Stern bedeutet jeden Tag, zweiter jeden Monat.

47  7 * * 0 /run/on/sunday

dann Sie tailund Ausgabe

echo 47  7 * * 0 /run/on/sunday

das wird *zu lokaler Datei erweitert.

Archemar
quelle