Kann mir bitte jemand einen Vorschlag machen, wie das geht?
Ich habe zwei Listen (beide mit SHA1-Summen und ihren relativen Dateinamen), die aber unterschiedlich formatiert sind. Hier ist ein Beispiel:
list01.txt
artist'ssomesong.mp3,3f1dfd39e88e00477483dfd578d5284f5490a0a5
hello(previous one).sh,55a5fdde4843fc2f9d9e691cb658b6389d698b22
mymovie [1989, director's cut].mov,4bdee0fc0eb7a3dbc5bbe2b65a02a1f9dc76c443
[etc...]
list02.txt
3f1dfd39e88e00477483dfd578d5284f5490a0a5 /path/to/my new music/album.wav
f77921adf6748f65fe688a5484ed901d4g9932hh /path/to/movies/[YEAR]/mymovie [1989, director's cut].mov
55a5fdde4843fc2f9d9e691cb658b6389d698b22 /path/to/scripts,regexs/hello(previous one).sh
[etc...]
Wie Sie sehen, ist der einzige gültige Eintrag für sha1sum 55a5fdde4843fc2f9d9e691cb658b6389d698b22
mit Dateinamen hello(previous one).sh
(2. Zeile in list01.txt
und 3. Zeile in list02.txt
).
Dateinamen und Pfade können Leerzeichen und Sonderzeichen enthalten (z. B.: ' " [ ] ( ) { } usw.).
Das Einzige, worüber Sie 100 % sicher sein können, ist, dass es immer als ; list01.txt
formatiert ist und immer (zwei Leerzeichen vor /) hat.
,sha1sum
list02.txt
sha1sum /
Wie im Titel dieser Frage möchte ich einif-Bedingungin einem Bash-Skript, das beide Listen auf Übereinstimmungen überprüft (TRUE ist, wennsowohl sha1sum als auch filename sind gleich) und wenn es sie findet, dann kopiert es jedes Vorkommen mit
cp $source $destination
source=reads the /path/to/filename from list02.txt
destination=/wherever/i/want/
Danke!
Antwort1
Annahmen:
- GNU-Tools sind vorhanden (nicht standardmäßige
xargs
Optionencp
, möglicherweise nicht funktionierender NUL-Trennzeichen in anderenawk
) - Die Länge des Hashes beträgt immer 40 Zeichen
- Es gibt immer zwei Leerzeichen zwischen Hash und Dateipfad in
list02.txt
- In beiden Dateien sind keine Pipe
|
-Zeichen vorhanden (sonst anderes Trennzeichen verwenden)
Erster Schritt, beide Dateien zusammenführen:
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' list01.txt | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' list02.txt | sort -t'|' -k1)
- Erste Datei: Trennzeichen
,
durch ersetzen|
und Datei nach dem zweiten Feld sortieren - Zweite Datei: Trennzeichen
(zwei Leerzeichen) durch ersetzen
|
und nach dem ersten Feld sortieren - Verbinde die Dateien im Hash-Feld
Ausgabe:
3f1dfd39e88e00477483dfd578d5284f5490a0a5|artist'ssomesong.mp3|/path/to/my new music/album.wav
55a5fdde4843fc2f9d9e691cb658b6389d698b22|hello(previous one).sh|/path/to/scripts,regexs/hello(previous one).sh
Verwenden Sie dann, awk
um zu testen, ob der Dateiname von Feld2 als Dateiname im letzten Feld vorhanden ist. Wenn dies zutrifft, drucken Sie das letzte Feld mit einem NUL-Trennzeichen und leiten Sie das Ergebnis weiter, um xargs
die Dateien in das Zielverzeichnis zu kopieren.
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' list01.txt | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' list02.txt | sort -t'|' -k1) \
| awk -F '|' '
{
fname1=$2; sub(/.*\//, "", fname1) # extract filename1
fname2=$3; sub(/.*\//, "", fname2) # extract filename2
}
fname1 == fname2{ printf $3 "\0" } # compare filenames, print filepath with NUL separator
' | xargs -r0 cp -n -t /path/to/destination
Kopieroptionen:
-n
vorhandene Dateien nicht überschreiben-t
Zielverzeichnis
Als Skript:
#!/bin/bash
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' "$1" | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' "$2" | sort -t'|' -k1) \
| awk -F '|' '
{
fname1=$2; sub(/.*\//, "", fname1) # extract filename1
fname2=$3; sub(/.*\//, "", fname2) # extract filename2
}
fname1 == fname2{ printf $3 "\0" } # compare filenames, print filepath with NUL separator
' | xargs -r0 cp -n -t "$3"
Führen Sie es wie folgt aus:
./script.sh list1 list2 /path/to/destination