"Christmas - the time to fix the computers of your loved ones" « Lord Wyrm

find, xargs, subshell, quotes...

that 05.01.2009 - 17:07 1088 6
Posts

that

Hoffnungsloser Optimist
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
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:

Code:
#!/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

Administrator
GNUltra
Avatar
Registered: Dec 2000
Location: ~
Posts: 12070
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
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
Zitat von COLOSSUS
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.

Zitat von COLOSSUS
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

Administrator
GNUltra
Avatar
Registered: Dec 2000
Location: ~
Posts: 12070
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
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
Zitat von COLOSSUS
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:
Code:
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

Administrator
GNUltra
Avatar
Registered: Dec 2000
Location: ~
Posts: 12070
http://johannes.truschnigg.info/tmp/thatmerge.bash

Code:
Usage: thatmerge DIR1 DIR2 [-f]
Ohne -f als argv[3] => dry-run, es wird also nichts wirklich geloescht.
Potenziell unsafe (d. h. nicht gestetest) bei \n in den Dateinamen.

that

Hoffnungsloser Optimist
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
Danke, das funktioniert gut. Ich hab zum Glück keine mehrzeiligen Dateinamen.

Fürs Archiv, hier ist der Code:

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.
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz