Ich versuche, den folgenden Code zu verwenden, um die letzte Datei wegzulassen, während der Rest in einen Zielordner kopiert wird:
ls -Art | tail -n 1 | xargs -I% cp !(%) ~/test_dst/folder1
Die ersten beiden Pipes holen den Namen der neuesten Datei, und dieser wird in xargs weitergeleitet, das ihn in den cp-Befehl einfügt, wo ich das Platzhalterzeichen ! von extglob verwende, um alles andere zu kopieren. Das Problem ist jedoch, dass dadurch jede Datei kopiert wird! Ich habe versucht, dies zu debuggen, indem ich ermittelt habe, was der erste Teil davon ausgibt (set -x ist aktiviert):
> ~/test_src/folder1$ ls -Art | tail -n 1
+ tail -n 1
+ ls --color=auto -Art
file4
Erfolg! Dies ist tatsächlich die richtige Datei! Ich habe dann versucht, sie mit dem Platzhalter auszudrucken:
> ~/test_src/folder1$ ls -Art | tail -n 1 | xargs -I% echo !(%)
+ tail -n 1
+ ls --color=auto -Art
+ xargs -I% echo file1 file2 file3 file4
file1 file2 file3 file4
und es enthält die ausgelassene Datei?? Ich habe versucht, den Befehl selbst fest zu codieren, und siehe da, es funktioniert tatsächlich richtig:
> ~/test_src/folder1$ echo !(file4)
+ echo file1 file2 file3
file1 file2 file3
was mache ich falsch? das ist im Grunde mein erster Versuch mit einem Shell-Skript und es macht mich verrückt, ich bin für jeden Tipp dankbar. Ich würde diese spezielle Methode lieber debuggen (das ist einfach die Art, wie ich es mir vorgestellt habe), aber ich bin definitiv offen für alle Vorschläge oder Ersatzmethoden, um das zu tun. Danke!
Antwort1
Ja, das wird nicht funktionieren. Die beiden Features, die Sie kombinieren möchten, werden in der falschen Reihenfolge interpretiert.
Extglobs sind eine Funktion der Shell (des Befehlsinterpreters), und wie Sie in der "Trace"-Ausgabe bemerkt haben, werden sie von bash erweitertVorder Befehl „xargs“ wird tatsächlich ausgeführt.
Wenn Extglobs – oder eigentlich alle Globs – unverändert an xargs (oder an „echo“ oder an „cp“) übergeben würden, wüsste es nicht, was es mit ihnen tun soll. Beispielsweise würde „cp“ denken, dass „!(%)“ oder „!(file1)“ tatsächlich der Name der zu kopierenden Datei ist.
xargs hingegen istnichtTeil der Shell – es ist ein eigenständiges Programm wie jedes andere, und Bash kennt weder die Bedeutung des „%“, noch weiß es, dass Sie einen anderen Befehl an xargs übergeben.
Wenn also das !(%)
Extglob erweitert wird, wird es nur erweiterteinmalfür den gesamten Befehl – nicht einmal für jeden xargs-Eingang, sondern nur einmal für das Literal, %
das Bash im Befehl sieht. Dies führt natürlich zualle Dateien, die nicht benannt sind%
.
Eine Möglichkeit, dies zu tun, besteht darin, xargs durch die eigenen Erweiterungen der Shell zu ersetzen – im Gegensatz zu xargs werden Shell-Variablen erweitertVorextglobs, wenn Sie also den Dateinamen in einer Variablen hätten ...
except_file=$(ls -Art | tail -n 1)
...das könntest du im Extglob verwenden:
cp !("$except_file") ~/test_dst/folder1
(Die beiden können zu kombiniert werden cp !("$(ls...)") ~/...
.)
Es ist auch möglich, das Gegenteil zu tun und xargs beizubehalten, aber das Extglob zu entfernen. Anstatt nur die letzte Datei abzurufen tail
und dann zusätzliche Schritte zu unternehmen, um alles andere abzurufen, können Sie direkt Folgendes abrufen:alles außerdie letzte verwendete Datei head
(negative Parameter für „head“ und „tail“ bedeuten „außer dem letzten N“):
ls -Art | head -n -1 | xargs -I% cp % ~/test_dst/folder1
oder überlassen Sie xargs die Aufgabe, eines cp
für alle Dateien gleichzeitig aufzurufen, vorausgesetzt, Ihr System verfügt über GNU Coreutils mit der cp -t <target>
Option:
ls -Art | head -n -1 | xargs -d '\n' cp -t ~/test_dst/folder1
Randbemerkung: Dateinamen unter Linux können Zeilenumbrüche enthalten. Viele der obigen Beispiele gehen davon aus, dass Sie keine solchen Dateinamen haben, weil Sie das wirklich nicht sollten – aber esIstDies ist beim Schreiben von Skripten zu berücksichtigen, die auf unbekannten Systemen eingesetzt werden sollen. Bei der „defensiven“ Programmierung werden normalerweise Tools wie find ... -print0
und verwendet xargs -0
, um mit durch Nullen getrennten Listen statt durch Zeilenumbrüche getrennten Listen zu arbeiten. (Shell-Platzhalter kommen damit im Allgemeinen gut zurecht.)