find, xargs, subshell, quotes...
that 05.01.2009 - 17:07 1087 6
that
Hoffnungsloser Optimist
|
Ich hab hier 2 Festplatten mit unterschiedlichen Versionen eines Directory-Trees. Ich möchte von der zweiten Festplatte alle Dateien löschen, die eine identische Kopie auf der ersten Festplatte haben. Folgendes Shellscript funktioniert fast, aber nicht ganz: #!/bin/bash
DELDIR=/mnt/1/other
DELLIST=deletelist.sh
find . -type f -print0 | xargs -0 -I! -n1 bash -c "[[ -f \"$DELDIR/!\" ]] && cmp \"!\" \"$DELDIR/!\" && echo \"rm !\" >> $DELLIST"
Wenn ein einem Dateinamen ein Backtick vorkommt, mag das die (Sub-)Shell nicht. Ersetze ich die gequoteten Doublequotes durch Singlequotes, gehen Dateinamen mit Singlequote nicht. Preisfrage: Wie bringt man das Ding dazu, mit allen Zeichen in Filenamen das Korrekte zu tun?
Bearbeitet von that am 05.01.2009, 21:16
|
COLOSSUS
AdministratorGNUltra
|
Vielleicht willst du dir mit app-misc/fdupes (siehe auch: http://en.wikipedia.org/wiki/Fdupes ) etwas Arbeit ersparen? Wenn nicht, mach ich mich wohl im Laufe dieses hoffentlich ruhigen Abends an einen Rewrite dieses Monsters
|
that
Hoffnungsloser Optimist
|
Vielleicht willst du dir mit app-misc/fdupes (siehe auch: http://en.wikipedia.org/wiki/Fdupes ) etwas Arbeit ersparen? Danke, das ist mir beim Suchen auch schon untergekommen (ich schreib doch nicht absichtlich Shellscripts wenn ich nicht unbedingt muss ), aber ich will ja nicht eine Menge von Dateien gegen sich selbst abgleichen, sondern zwei Mengen gegeneinander. So wie ich die Doku verstanden hab, wirft fdupes alle Dateien in einen Topf und sucht dann welche mit gleichen Hashes. Wenn nicht, mach ich mich wohl im Laufe dieses hoffentlich ruhigen Abends an einen Rewrite dieses Monsters Monster? Das ist eh nur ein Einzeiler. Aber jedesmal, wenn ich mich an Shellscripting wage, dauerts viel länger als erwartet, wird ziemlich unlesbar und funktioniert hinterher auch nicht 100%ig (das mit "rm" in die Datei schreiben war übrigens auch keine gute Idee, weil da schon wieder Quotes fehlen, z.B. steht dann drin "rm That's bad quoting.txt" - lässt sich zwar mit cut und noch einem xargs umgehen, aber das sollte auch eleganter gehen).
|
COLOSSUS
AdministratorGNUltra
|
Ok, ein paar Fragen zum Problem:
Kannst du dir sicher sein, dass Dateien in den beiden Verzeichnissen, welche keinen Inhalt haben, gefahrlos geloscht werden koennen? Ist davon auszugehen, dass nicht mehr als zwei Dateien (jeweils eine pro Verzeichnis) inhaltsgleich sind? Ist davon auszugehen, dass inhalsgleiche Dateien, die im selben Verzeichnis(baum) vorkommen, geloescht werden duerfen? Haben nichtleere inhaltsgleiche Dateien auch immer den selben Dateinamen in beiden Verzeichnissen?
|
that
Hoffnungsloser Optimist
|
Ok, ein paar Fragen zum Problem:
Kannst du dir sicher sein, dass Dateien in den beiden Verzeichnissen, welche keinen Inhalt haben, gefahrlos geloscht werden koennen? Ist davon auszugehen, dass nicht mehr als zwei Dateien (jeweils eine pro Verzeichnis) inhaltsgleich sind? Ist davon auszugehen, dass inhalsgleiche Dateien, die im selben Verzeichnis(baum) vorkommen, geloescht werden duerfen? Haben nichtleere inhaltsgleiche Dateien auch immer den selben Dateinamen in beiden Verzeichnissen? 1. Ich will in einem der beiden Verzeichnisse überhaupt nichts löschen. 2. Es kann auf einer Seite mehrere Dateien mit gleichem Inhalt geben. 3. Nein. 4. Ja. Also als Pseudocode: for each file f in $dir1 (incl. all subdirs)
if $dir2/$f exists
if $dir1/$f and $dir2/$f have identical content
delete file $dir2/$f
endif
endif
endfor
In irgendeiner Scriptsprache hätt ich das locker in ein paar Minuten geschrieben, aber mich würd halt interessieren wie das mit der Shell geht.
|
COLOSSUS
AdministratorGNUltra
|
|
that
Hoffnungsloser Optimist
|
Danke, das funktioniert gut. Ich hab zum Glück keine mehrzeiligen Dateinamen. Fürs Archiv, hier ist der Code: #!/usr/bin/env bash
unset ACTION DEST SRC DIR_1 DIR_2;
export DIR_1="${1}";
export DIR_2="${2}";
if [ ! -d "${DIR_1}" ] || [ ! -d "${DIR_2}" ]; then
exit 1;
fi
[[ "${3}" == "-f" ]] && ACTION="/bin/rm " || ACTION="echo rm "
while read SRC;
do
DEST="${SRC/${DIR_1}/${DIR_2}}";
[[ -f "${DEST}" ]] && cmp -s "${DEST}" "${SRC}" && ${ACTION} "${DEST}" && echo "${DEST} unlinked."
done < <(find "${DIR_1}" -type f -print)
Der Trick in der letzten Zeile ( http://tldp.org/LDP/abs/html/process-sub.html) war mir neu. Falls sich zufällig irgendwer mit der Windows Powershell auskennt: Mich würde interessieren, wie das damit geht. Auch wenn ichs jetzt nicht brauche.
|