it-swarm.com.de

Wie verschiebe ich ein Verzeichnis in ein Verzeichnis mit demselben Namen?

Ich habe ein Verzeichnis foo mit mehreren Dateien:

.
└── foo
    ├── a.txt
    └── b.txt

und ich möchte es in ein Verzeichnis mit dem gleichen Namen verschieben:

.
└── foo
    └── foo
        ├── a.txt
        └── b.txt

Ich erstelle gerade ein temporäres Verzeichnis bar, verschiebe foo in bar und benenne bar anschließend in foo um:

mkdir bar
mv foo bar
mv bar foo

Aber das fühlt sich etwas umständlich an und ich muss einen Namen für bar auswählen, der noch nicht vergeben ist.

Gibt es einen eleganteren oder direkteren Weg, um dies zu erreichen? Ich bin auf macOS, wenn das wichtig ist.

32
Stefan

Um sicher ein temporäres Verzeichnis im aktuellen Verzeichnis mit einem Namen zu erstellen, der noch nicht vergeben ist, können Sie mktemp -d So verwenden:

tmpdir=$(mktemp -d "$PWD"/tmp.XXXXXXXX)   # using ./tmp.XXXXXXXX would work too

Der Befehl mktemp -d Erstellt ein Verzeichnis unter dem angegebenen Pfad, wobei die X- am Ende des Pfadnamens durch zufällige alphanumerische Zeichen ersetzt werden. Es wird der Pfadname des erstellten Verzeichnisses zurückgegeben, und wir speichern diesen Wert in tmpdir.1

Diese tmpdir -Variable kann dann verwendet werden, wenn Sie das gleiche Verfahren ausführen, das Sie bereits ausführen, wobei bar durch "$tmpdir" Ersetzt wird:

mv foo "$tmpdir"
mv "$tmpdir" foo
unset tmpdir

Das unset tmpdir Am Ende entfernt nur die Variable.


1Normalerweise kann man sollte die Umgebungsvariable TMPDIR auf einen Verzeichnispfad setzen, in dem temporäre Dateien oder Verzeichnisse mit mktemp erstellt werden sollen, aber das Dienstprogramm unter macOS scheint diesbezüglich auf subtile Weise anders zu funktionieren als dasselbe Dienstprogramm auf anderen BSD-Systemen und erstellt das Verzeichnis an einem völlig anderen Ort. Das obige würde jedoch unter macOS funktionieren. Die Verwendung der etwas bequemeren Funktion tmpdir=$(TMPDIR=$PWD mktemp -d) oder sogar tmpdir=$(TMPDIR=. mktemp -d) wäre unter macOS nur dann ein Problem, wenn sich das temporäre Standardverzeichnis auf einer anderen Partition befindet und foo Verzeichnis enthielt viele Daten (dh es wäre langsam).

21
Kusalananda

Unter macOS können Sie den Befehl rename (ein Perl-Skript) mit Homebrew installieren:

brew install rename

Verwenden Sie dann -p (A la mkdir), um die erforderlichen Verzeichnisse zu erstellen, und -A, Um ein Präfix hinzuzufügen:

% mkdir -p foo/bar; touch foo/{a,b}.txt foo/bar/c.txt
% rename -p -A foo/ foo/*
% tree foo
foo
└── foo
    ├── a.txt
    ├── b.txt
    └── bar
        └── c.txt

Führen Sie mit -n Aus, um Änderungen ohne Umbenennung anzuzeigen (Trockenlauf):

% rename -p -A foo/ foo/* -n
'foo/a.txt' would be renamed to 'foo/foo/a.txt'
'foo/b.txt' would be renamed to 'foo/foo/b.txt'
'foo/bar' would be renamed to 'foo/foo/bar'

Wenn Sie Punktdateien haben, damit ein einfacher * Diese nicht aufnimmt, verwenden Sie andere Methoden mit rename:

  • Verwenden Sie mit bash (mis) GLOBIGNORE, um * Zu erhalten, damit Punktdateien übereinstimmen:

    $ GLOBIGNORE=.; rename -p -A foo/ foo/* -n
    'foo/.baz' would be renamed to 'foo/foo/.baz'
    'foo/a.txt' would be renamed to 'foo/foo/a.txt'
    'foo/b.txt' would be renamed to 'foo/foo/b.txt'
    'foo/bar' would be renamed to 'foo/foo/bar'
    
  • Oder verwenden Sie find mit -print0 Und rename mit -0:

    % find foo -mindepth 1 -maxdepth 1 -print0 | rename -0 -p -A foo/ -n
    Reading filenames from STDIN
    Splitting on NUL bytes
    'foo/b.txt' would be renamed to 'foo/foo/b.txt'
    'foo/a.txt' would be renamed to 'foo/foo/a.txt'
    'foo/bar' would be renamed to 'foo/foo/bar'
    'foo/.baz' would be renamed to 'foo/foo/.baz'
    
10
muru

Solange der Inhalt nicht ausreicht, um die maximalen Parametergrenzen zu überschreiten (und Ihnen eine "akzeptable" Fehlermeldung nichts ausmacht), muss er nicht komplizierter sein:

mkdir foo/foo
mv foo/* foo/foo

Änderung zum Umgang mit versteckten Dateien:

mkdir foo/foo
mv foo/{.,}* foo/foo
10
bxm

Ich schlage den umgekehrten Weg vor. Verschieben Sie nicht das Verzeichnis, sondern nur dessen Inhalt:

.
└── foo
    ├── a.txt
    └── b.txt
mkdir foo/foo
.
└── foo
    ├── foo
    ├── a.txt
    └── b.txt
cd foo
mv $(ls | grep -v '^foo$') foo
cd -
.
└── foo
    └── foo
        ├── a.txt
        └── b.txt

Wenn Sie Bash haben, können Sie auch tun

shopt -s extglob
cd foo
mv !(foo) foo
cd -

(wie beschrieben hier ) um zu vermeiden, dass ls und grep ausgeführt werden.

Der Vorteil dieser Lösung ist, dass es wirklich einfach ist.

Nachteile (wie in den Kommentaren ausgeführt):

  • Versteckte Dateien können nicht verarbeitet werden (ls -A Behebt das Problem, aber nicht ls -a)
  • Dateinamen mit Leerzeichen können nicht verarbeitet werden (können durch Anführungszeichen behoben werden: mv "$(ls | grep -v '^foo$')" foo
  • Dateinamen mit Zeilenumbrüchen und anderen Sonderzeichen können nicht verarbeitet werden

Die meisten Nachteile können mit einem Bash-Trick behoben werden. Wenn man jedoch mit verrückten Dateinamen umgehen muss, ist es besser, einen robusteren Ansatz zu verwenden, wie in anderen Antworten beschrieben.

8
Adam Trhon

Du hast es schon so ziemlich genagelt. Sie können einen anderen Namen für das transiente Verzeichnis auswählen, z. B. den Zielnamen mit dem aktuellen Datum/der aktuellen Uhrzeit in Nanosekunden und unsere PID als zusammengesetztes Suffix. Dies setzt jedoch voraus, dass das Verzeichnis noch nicht vorhanden ist:

dir=foo                         # The directory we want to nest
now=$(date +'%s_%N')            # Seconds and nanoseconds
mkdir "$dir.$now.$$"            # Create a transient directory
mv -f "$dir" "$dir.$now.$$/"    # Move our directory inside transient directory
mv -f "$dir.$now.$$" "$dir"     # Rename the transient directory to the name with which we started

Wenn Sie eine garantierte robuste Lösung wünschen, würde ich das mkdir durchlaufen, bis es erfolgreich war

now=$(date +'%s_%N')
while ! mkdir -m700 "$dir.$now.$$" 2>/dev/null
do
    sleep 0.$$
    now=$(date +'%s_%N')
done
6
roaima
mkdir foo/foo && mv foo/!(foo) foo/foo

Sie müssen in das Verzeichnis cd, in dem sich der Quellordner (foo) befindet.

Führen Sie dann den obigen Befehl aus. Es wird ein gleichnamiger Ordner erstellt und der Inhalt des übergeordneten foo in das untergeordnete foo-Verzeichnis verschoben (mit Ausnahme des untergeordneten Verzeichnisses, daher die Bezeichnung!).

Wenn das untergeordnete foo-Verzeichnis bereits vorhanden ist, können Sie den ersten Teil ignorieren und einfach den Befehl mv ausführen:

mv foo/!(foo) foo/foo

Unter MacOS müssen Sie möglicherweise die Option extglob festlegen:

shopt -s extglob
6
masterl

Zusätzlich zu den obigen Vorschlägen möchten Sie möglicherweise rsync auschecken. Denken Sie immer daran, die Option --dry-run Mit rsync zu verwenden, bevor Sie sie ohne ausführen, bevor ich überhaupt anfange. Der --dry-run Sagt Ihnen, was im wirklichen Leben passieren würde.

rsync bietet viele Optionen, die nicht nur beim Kopieren/Verschieben von Dateien helfen, sondern auch den Prozess beschleunigen können. Hier ist eine Möglichkeit, Ihre Arbeit zu erledigen. Die Option --remove-source-files Löscht Dateien nach dem Verschieben aus der Quelle (dies ist eigentlich eine Kopie, gefolgt von einem Löschen). Beachten Sie, dass der Befehl rsync zuerst den Inhalt von foo liest, das Verzeichnis foo im vorhandenen Verzeichnis foo erstellt und dann den Kopiervorgang startet. Es endet mit dem Löschen der Quelldateien in foo. Achtung: Achten Sie besonders auf die Schrägstriche für die Verzeichnisnamen.

Es gibt andere Überlegungen, auf die viele oben hingewiesen haben (wie Symlinks), aber ein man rsync Hilft Ihnen bei der Auswahl der gewünschten Optionen. Ein Hinweis hier: Da Sie nach einer Lösung für Dateien gefragt haben, funktioniert dies. Dadurch werden keine leeren Verzeichnisse entfernt, die zurückbleiben. Mir ist kein Weg bekannt, dies ohne einen zusätzlichen Schritt zu tun. Wenn also jemand etwas hinzufügen kann, danke im Voraus!

mkdir foo
cd foo
touch aaa bbb ccc ddd
cd ..
rsync -av --remove-source-files foo foo/

a = Archivierungsmodus

v = ausführlich

2
Hopping Bunny

Berühren Sie einfach nicht foo und Sie haben keine Probleme mit identischen Namen. Erstellen Sie stattdessen ein Verzeichnis mit demselben Namen und verschieben Sie alles dorthin. Verwenden Sie dazu den Trick $ _ und &&, um daraus einen Einzeiler zu machen:

cd foo && mkdir $_ && mv * $_

Dies führt zu einem harmlosen Fehler (mv: foo in foo umbenennen/foo: Ungültiges Argument), den Sie ignorieren können.

Vorteil gegenüber anderen Antworten: Sie müssen den Verzeichnisnamen nur einmal eingeben, damit Sie mit Tippfehlern keine Fehler machen können.


sie können dies mit anderen Antworten mischen, um eine noch bessere Wirkung zu erzielen:

  • mv {.,}* $_ Kümmert sich um versteckte Dateien (auf Kosten weiterer Fehler, die Sie auch ignorieren können)
  • mv ./!(foo) vermeidet die Fehlermeldung, jedoch nur, wenn Sie shopt -s extglob setzen.
2
Tom

Das ist eigentlich täuschend einfach und ich bin schockiert, dass noch niemand diese Antwort angeboten hat. Also gehe ich zum Beispiel wie folgt:.

$ mkdir foo
$ mkdir bar
$ mkdir ./bar/foo
$ touch ./bar/foo/file1 && touch ./bar/foo/file2 && touch ./bar/foo/file3
$ ls ./
bar/ foo/
$ ls ./bar/foo
file1 file2 file3

Jetzt kommt der magische Teil

$ mv ./bar/foo ./foo/
$ ls ./foo/
foo/
$ ls ./foo/foo/
file1 file2 file3

Warum funktioniert das jetzt? Ich kann Ihnen nicht die genaueste Antwort geben, da ich nur ein Neuling bin - aber ich verstehe, dass es darauf ankommt, wie nachgestellte / - Zeichen in Ordnern behandelt werden.

Schauen Sie sich diesen mv-Befehl genau an

$ mv ./bar/foo ./foo/

Beachten Sie, wie der Zielordner von Dateien ohne ein abschließendes / Angegeben wird, während das Ziel, das den Namen des Zielordners teilt, mit einem abschließenden / Angegeben wurde. Dies ist Ihr magisches Ticket und kann Sie in den Arsch beißen, wenn Sie nicht aufpassen.

Es tut mir leid, dass ich derzeit keine ausführlichere Antwort anbieten kann, aber vielleicht wird sich jemand, der sich besser auskennt, die Freiheit nehmen, meine Antwort zu bearbeiten.

In jedem Fall scheint dies die einfachste und unkomplizierteste Lösung zu sein, mit einer einzigen Verwendung des Befehls ls und ohne Erstellung von Zwischenordnern.

Hoffe es funktioniert für dich, Prost!

0
PowerLuser