UNIX-Shell-Scripting: Wie verschiebe ich Dateien rekursiv in ein Verzeichnis?


8

Ich habe eine große Anzahl kleiner Dateien, f, die in einer Verzeichnisstruktur wie folgt angeordnet sind:

/A/B/C/f

Es gibt 11 Verzeichnisse auf der Ebene von A mit jeweils etwa 100 Verzeichnissen auf der Ebene von B mit jeweils etwa 30 Verzeichnissen auf der Ebene von C mit jeweils einer Datei f.

Wie verschiebe ich alle Dateien um eine Ebene? Zum Beispiel angesichts dieser Gruppe von Dateien ...

/ A / B / C / f1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

Ich möchte, dass das Verzeichnis /A/B/4 Dateien enthält, f1 bis f4. Das Entfernen von Verzeichnissen C ist nicht erforderlich.

Ich hoffe , dies wird ein Problem gelöst, möglicherweise mit find, xargsund whatnot. Irgendwelche Ideen?

Prost,

James

Antworten:


9

Es ist ziemlich einfach mit GNU find (wie unter Linux) oder jedem anderen Fund, der Folgendes unterstützt -execdir:

find A -type f -execdir mv -i {} .. \;

Mit einem Standard find:

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

Mit zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

Wenn die Verzeichnisstruktur immer dieselbe Verschachtelungsebene hat, benötigen Sie keine rekursive Durchquerung (entfernen Sie jedoch zuerst leere Verzeichnisse):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

1

Für diesen Satz von Dateien würde dies Folgendes tun:

$ cd /A/B/C/
$ mv ./* ../

Aber ich erwarte, dass Ihr Problem etwas komplizierter ist ... Ich kann darauf nicht antworten ... Ich bin mir nicht ganz sicher, wie Ihre Verzeichnisstruktur ist ... Könnten Sie das klarstellen?


Dies funktioniert natürlich für jedes Verzeichnis / A / B / C, aber da ich ungefähr 30000 solcher Verzeichnisse habe, hoffe ich auf einen Befehl, der oben in der Hierarchie ausgeführt werden kann, um sie alle gleichzeitig zu erledigen.
16.

1
@ jl6 Ich sehe, und nur Dateien müssen verschoben werden? C muss also nicht nach B und B nicht nach A usw. wechseln.
Stapelüberlauf ist

Das ist richtig - der Vorgang sollte nur für Dateien auf der untersten Ebene der Hierarchie ausgeführt und nur um eine Ebene nach oben verschoben werden.
16.

0

Meine erste Vermutung ist

$ find A -type f -exec mv {} .. \;  

Solange Sie nicht angeben -depth, sollte es in Ordnung sein. Ich habe es NICHT ausprobiert, also testen Sie es zuerst, bevor Sie sich dazu verpflichten.


Was macht find, wenn zwei Dateien mit demselben Namen vorhanden sind?

Suchen ist nicht das Problem, sondern der eingebettete Befehl "mv". Betrogene Namen wären nicht gut, da mv nicht umbenennen, sondern überschreiben wird. Sie könnten mv -i verwenden, um ein wenig zu helfen, aber bei mehr als 30.000 Dateien kann dies schmerzhaft sein. Wenn Sie so viel Kontrolle über die Situation haben müssen, benötigen Sie möglicherweise ein Programm, das in einer Skriptsprache geschrieben ist (meine Wahl wäre Python, YMMV).
Hotei

Ich habe keine "Backups" mit mv verwendet, aber auf der Manpage sieht es so aus, als ob dies eine Lösung für Ihr Problem mit betrogenen Namen sein könnte. "finde A-Typ f -exec mv --backup nummeriert {} .. \;" könnte funktionieren (beachten Sie, dass dies ein doppelter Strich in --backup ist)
hotei

Das angegebene Suchbeispiel verschiebt alle Dateien auf der untersten Ebene in ein Verzeichnis oberhalb von A (ich glaube, das ".." wird von der Shell interpretiert, bevor es an find / mv übergeben wird?). Ich brauche die Dateien, um nur eine Ebene in der Hierarchie nach oben zu verschieben. Eine weitere Klarstellung: Es gibt keine doppelten Dateinamen, solange sich die Dateien nur um eine Ebene nach oben bewegen.
16.

@hotei: Die mvBefehle werden im aktuellen Verzeichnis ausgeführt, tun ..also nicht das, was Sie hoffen. Siehe meine Antwort für Lösungen. @ jl6: Die Shell hat nichts damit zu tun .., das wird vom Kernel erledigt.
Gilles 'SO - hör auf böse zu sein'

0

Wenn Sie nur bewegen , Dateien möchten , die in Blatt Verzeichnisse sind (dh Sie nicht verschieben wollen , /A/B/fileum , /Awenn BVerzeichnisse enthält) , dann sind hier ein paar Möglichkeiten , dies zu tun:

Beides erfordert dies

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

Dieser funktioniert:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

Dies wird schneller sein, aber es mag keine leeren Quellverzeichnisse:

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.