1.grep | xargs mv

1.grep | xargs mv

Ich habe einige solcher Dateien in einem Verzeichnis

00.01.02 0000 some filename 1.pdf
00.01.02 0000 some filename 1.txt
02.03.07 0163 another filename 2.pdf
02.03.07 0163 another filename 2.txt

und ich muss alle TXT-Dateien finden, die eine bestimmte Zeichenfolge enthalten, und dann diese TXT-Datei und die entsprechende PDF-Datei in ein anderes Verzeichnis verschieben.

Wie lässt sich dies am besten erledigen?

Antwort1

Ich habe 3 Optionen für Sie. Die Grundideen sind:

  1. grep | xargs mv
  2. grep | parallel mv
  3. find -exec grep -q -exec mv

1.grep | xargs mv

Verwenden Sie grepund xargs:

grep -FlZ "some string" *.txt \
| xargs -0 -I{} sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/directory' xargs-sh {}

grep:

  • -FÜbereinstimmung mit einer vollständigen Zeichenfolge, nicht mit einem Muster
  • -lAusgabe nur mit übereinstimmenden Dateinamen
  • -ZNull-Trennzeichen zwischen Dateinamen ausgeben (Das ist wichtig, weil Dateinamen Zeilenumbrüche enthalten dürfen, Sie sich also nicht auf einen Zeilenumbruch als Trennzeichen verlassen können)

xargs:

  • -0liest nullbegrenzte Daten

2.grep | parallel mv

Mit xargsmüssen Sie eine Subshell aufrufen, sh -cum den entsprechenden PDF-Dateinamen abrufen zu können.

Stattdessen können Sie auch GNU parallelcoole Features nutzen!!

grep -FlZ "some string" *.txt \
| parallel -0 -j1 echo mv {} {.}.pdf some/other/directory
  • -j1immer nur ein Auftrag gleichzeitig
  • {}der Dateiname
  • {.}Dateiname ohne Erweiterung

3.find -exec grep -q -exec mv

Und noch eine Alternative mit find:

find . -maxdepth 1 -name "*.txt" \
-exec grep -Fq "some string" {} \; \
-exec sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/location' find-sh {} \;
  • -maxdepth 1nicht rekursiv
  • grep -qum nur den Fehlercode anstelle der Dateinamen zu erhalten, -execwird der zweite nur ausgeführt, wenn der erste erfolgreich war.
  • Die zweite Option -execist grundsätzlich die gleiche wie bei xargsder ersten.
  • wir müssen keine durch Nullen getrennte Ausgabe machen, weil wir keine Dateinamen übergeben, finddas kümmert sich.

Entfernen Sie für jede Option das, echowenn Sie mit der Ausgabe zufrieden sind.

Antwort2

Zerlegen Sie das Problem in Einzelteile.

„Ich muss alle Textdateien im aktuellen Verzeichnis finden, die eine bestimmte Zeichenfolge enthalten.“

grep -F "text string just as written (no regex)" *.txt

"Ich will dasDateinamendieser Dateien"

grep -l -F "text string" *.txt

„Für jede dieser Dateien …“

for...each verwenden Sie ein while/do/doneKonstrukt, an das Sie die Dateiliste übergeben. Für jede Datei können Sieetwas, die Zugriff auf die Variable haben $file:

grep -l -F "text string" *.txt \
| while IFS= read -r file ; do
...something...
done

Das „Etwas“ ist

„...verschieben Sie die txt-Datei und die entsprechende pdf-Datei...“

Sie benötigen also den Basisnamen ohne ".txt"

BASE=$( basename ${file} .txt )

Und zum Schluss alles zusammenfassen:

grep -l -F "text string" *.txt \
| while IFS= read -r file ; do \
    BASE=$( basename ${file} .txt )
    mv ${BASE}.txt /some/other/dir
    mv ${BASE}.pdf /some/other/dir
done

(Beachten Sie, dass hiervon ausgegangen wird, dass jederDateiist ohne Pfad, weil basenamees sonst abgestreift würde).

Antwort3

Wenn Sie keinen parallelBefehl haben, machen Sie mit zwei xargswie unten

grep -l 'search string' *.txt | xargs -I {} basename {} .txt | xargs -I {} cp {}.pdf /destination-directory

verwandte Informationen