Gibt es eine Möglichkeit, mv dazu zu bringen, das Verzeichnis zu erstellen, in das verschoben werden soll, wenn es nicht vorhanden ist?

275

Wenn ich mich also in meinem Home-Verzeichnis befinde und foo.c nach ~ / bar / baz / foo.c verschieben möchte, diese Verzeichnisse jedoch nicht vorhanden sind, gibt es eine Möglichkeit, diese Verzeichnisse automatisch zu erstellen Sie müssten nur tippen

mv foo.c ~/bar/baz/ 

und alles würde klappen? Es scheint, als könnten Sie mv mit einem einfachen Bash-Skript aliasen, das prüft, ob diese Verzeichnisse vorhanden sind, und wenn nicht, mkdir und dann mv aufruft, aber ich dachte, ich würde prüfen, ob jemand eine bessere Idee hat.

Paul Wicks
quelle
Siehe auch
kvantour

Antworten:

294

Wie wäre es mit diesem Einzeiler (in Bash):

mkdir --parents ./some/path/; mv yourfile.txt $_

Aufschlüsselung:

mkdir --parents ./some/path

Erstellt das Verzeichnis (einschließlich aller Zwischenverzeichnisse). Danach:

mv yourfile.txt $_

Verschiebt die Datei in dieses Verzeichnis ($ _ wird auf das letzte Argument erweitert, das an den vorherigen Shell-Befehl übergeben wurde, dh auf das neu erstellte Verzeichnis).

Ich bin mir nicht sicher, wie weit dies in anderen Shells funktionieren wird, aber es könnte Ihnen einige Ideen geben, wonach Sie suchen sollten.

Hier ist ein Beispiel mit dieser Technik:

$ > ls
$ > touch yourfile.txt
$ > ls
yourfile.txt
$ > mkdir --parents ./some/path/; mv yourfile.txt $_
$ > ls -F
some/
$ > ls some/path/
yourfile.txt
KarstenF
quelle
32
Schön mit dem '$ _'.
dmckee --- Ex-Moderator Kätzchen
1
Warum die Besessenheit, ./überall überflüssig zu sein ? Die Argumente sind keine Befehle in PATH ohne das .Verzeichnis ...
Jens
3
für die Neulinge können
Eltern
8
Seltsamerweise --parentsist es unter macOS 10.13.2 nicht verfügbar. Sie müssen verwenden -p.
Joshua Pinter
1
Funktioniert nicht, wenn eine Datei verschoben wird, z. B. ./some/path/foo. In diesem Fall sollte der Befehl sein:mkdir -p ./some/path/foo; mv yourfile.txt $_:h
hhbilly
63
mkdir -p `dirname /destination/moved_file_name.txt`  
mv /full/path/the/file.txt  /destination/moved_file_name.txt
Billmc
quelle
2
Ich bin der einzige, der erkennt, dass dieser Code keinen Sinn ergibt. Sie erstellen rekursiv den absoluten Pfad zur vorhandenen Datei (was bedeutet, dass dieser Pfad bereits vorhanden ist) und verschieben die Datei dann an einen Speicherort (der in Ihrem Beispiel nicht vorhanden ist).
Vdegenne
1
@ user544262772 GNU coreutils ' dirnamebenötigt kein Argument, es ist reine String-Manipulation. Ich kann nicht für andere Distributionen sprechen. An diesem Code ist nichts auszusetzen.
TroyHurts
Dies ist die Antwort, die sich am einfachsten automatisieren lässt, denke ich. for f in *.txt; do mkdir -p `dirname /destination/${f//regex/repl}`; mv "$f" "/destination/${f//regex/repl}; done
Kyle
23

Speichern Sie als Skript mit dem Namen mv oder mv.sh.

#!/bin/bash
# mv.sh
dir="$2"
tmp="$2"; tmp="${tmp: -1}"
[ "$tmp" != "/" ] && dir="$(dirname "$2")"
[ -a "$dir" ] ||
mkdir -p "$dir" &&
mv "$@"

Oder setzen Sie am Ende Ihrer ~ / .bashrc-Datei eine Funktion ein, die die Standard-MV auf jedem neuen Terminal ersetzt. Durch die Verwendung einer Funktion kann bash den Speicher behalten, anstatt jedes Mal eine Skriptdatei lesen zu müssen.

function mv ()
{
    dir="$2"
    tmp="$2"; tmp="${tmp: -1}"
    [ "$tmp" != "/" ] && dir="$(dirname "$2")"
    [ -a "$dir" ] ||
    mkdir -p "$dir" &&
    mv "$@"
}

Diese basieren auf der Vorlage von Chris Lutz.

Sepero
quelle
3
Dies beantwortet die Frage meiner Meinung nach am genauesten. Der Benutzer möchte einen erweiterten mvBefehl. Besonders nützlich für Skripte, dh Sie möchten die Überprüfungen nicht unbedingt ausführen und mkdir -pjederzeit ausführen, wenn Sie sie benötigen mv. Da ich jedoch das Standardfehlerverhalten für möchte mv, habe ich den Funktionsnamen in geändert, mvpdamit ich weiß, wann ich Verzeichnisse erstellen kann.
Brian Duncan
Scheint die beste Lösungsidee zu sein, aber die Implementierung stürzt häufig ab.
Ulises Layera
2
@UlisesLayera Ich habe den Algorithmus geändert, um ihn robuster zu machen. Es sollte jetzt nicht abstürzen
Sepero
Könnte jemand bitte erklären, was es bedeutet, dass [ ! -a "$dir" ]ich Experimente durchgeführt habe, bei denen die rechte Hälfte wahr und falsch ist, beide als wahr bewertet. Für andere scheint tmp = "$ {tmp: -1}" das letzte Zeichen der Datei zu erfassen, um sicherzustellen, dass es sich nicht um einen Pfad handelt (/)
bazz
@bazz Es sollte "wenn $ dir nicht existiert, dann mach weiter" sein. Leider haben Sie Recht, es gibt einen Fehler bei der Verwendung von "! -A", und ich weiß nicht warum. Ich habe den Code ein wenig bearbeitet und sollte jetzt funktionieren.
Sepero
13

Sie können verwenden mkdir:

mkdir -p ~/bar/baz/ && \
mv foo.c ~/bar/baz/

Ein einfaches Skript, um es automatisch zu machen (ungetestet):

#!/bin/sh

# Grab the last argument (argument number $#)    
eval LAST_ARG=\$$#

# Strip the filename (if it exists) from the destination, getting the directory
DIR_NAME=`echo $2 | sed -e 's_/[^/]*$__'`

# Move to the directory, making the directory if necessary
mkdir -p "$DIR_NAME" || exit
mv "$@"
Strager
quelle
Wenn ich "$ dirname ~? Bar / baz /" starte, erhalte ich "/ home / dmckee / bar", was Sie hier nicht wollen ...
dmckee --- Ex-Moderator Kätzchen
@dmckee, Ah, du hast recht. Irgendeine Idee, wie man das löst? Wenn Sie ~ / bar / baz eingeben, möchten Sie entweder (a) nach ~ / bar / kopieren und in baz umbenennen oder (b) nach ~ / bar / baz / kopieren. Was ist das bessere Werkzeug für den Job?
Strager
@dmckee, ich habe regexp / sed verwendet, um eine Lösung zu finden. Funktioniert es nach Ihren Wünschen? =]
Strager
Schön, außer dass $ 2 nicht das letzte Argument ist, es sei denn $ # = 2. Ich habe ein Programm, la, das sein letztes Argument druckt. Ich habe es in einer Version des Befehls cp verwendet (um das aktuelle Verzeichnis hinzuzufügen, wenn das letzte Argument kein Verzeichnis war). Sogar moderne Muscheln unterstützen nur $ 1 .. $ 9; Ein Perl-Skript ist möglicherweise besser.
Jonathan Leffler
@Leffler, nicht wahr - Ich weiß, dass Bash mehr als 9 Argumente unterstützt (die Verwendung von $ {123} ist eine Methode). Ich kenne Perl nicht, also zögern Sie nicht, selbst eine Antwort zu geben. =]
Strager
7

Es klingt wie die Antwort ist nein :). Ich möchte nicht wirklich einen Alias ​​oder eine Funktion erstellen, nur um dies zu tun, oft, weil es einmalig ist und ich bereits dabei bin, das zu tippenmv Befehl , aber ich habe etwas gefunden, das dafür gut funktioniert:

mv *.sh  shell_files/also_with_subdir/ || mkdir -p $_

Wenn dies mvfehlschlägt (dir existiert nicht), wird das Verzeichnis erstellt (dies ist das letzte Argument für den vorherigen Befehl $_). Führen Sie einfach diesen Befehl aus, upum ihn erneut auszuführen, und diesmal mvsollte dies erfolgreich sein.

Klopfen
quelle
5

Das folgende Shell-Skript vielleicht?

#!/bin/sh
if [[ -e $1 ]]
then
  if [[ ! -d $2 ]]
  then
    mkdir --parents $2
  fi
fi
mv $1 $2

Das ist der grundlegende Teil. Möglicherweise möchten Sie ein wenig hinzufügen, um nach Argumenten zu suchen, und Sie möchten möglicherweise, dass sich das Verhalten ändert, wenn das Ziel vorhanden ist oder das Quellverzeichnis vorhanden ist oder nicht vorhanden ist (dh etwas nicht überschreiben, das nicht vorhanden ist). .

Chris Lutz
quelle
1
Mit Ihrem Code wird die Verschiebung nicht ausgeführt, wenn das Verzeichnis nicht vorhanden ist!
Strager
1
Und Sie haben die "-p" -Flagge für mkdir verpasst.
dmckee --- Ex-Moderator Kätzchen
Wenn ein Verzeichnis nicht vorhanden ist, warum sollten Sie es erstellen, wenn Sie es nur verschieben möchten? (-p Flagge behoben)
Chris Lutz
Ich meine, wenn Sie das Skript ausführen und das Verzeichnis $ 2 nicht existiert, wird es erstellt, aber die Datei wird nicht kopiert.
Strager
Er gab ein einfaches Konstrukt und fügte Informationen hinzu, wie es erweitert werden sollte. Warum abstimmen?!
Ypnos
5

Der Befehl rsync kann den Trick nur ausführen , wenn das letzte Verzeichnis im Zielpfad nicht vorhanden ist, z. B. für den Zielpfad von ~/bar/baz/if barexistiert, bazaber nicht vorhanden ist. Dann kann der folgende Befehl verwendet werden:

rsync -av --remove-source-files foo.c ~/bar/baz/

-a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
-v, --verbose               increase verbosity
--remove-source-files   sender removes synchronized files (non-dir)

In diesem Fall wird ein bazVerzeichnis erstellt, wenn es nicht vorhanden ist. Aber wenn beides barund baznicht vorhanden ist, schlägt rsync fehl:

sending incremental file list
rsync: mkdir "/root/bar/baz" failed: No such file or directory (2)
rsync error: error in file IO (code 11) at main.c(657) [Receiver=3.1.2]

Grundsätzlich sollte es also sicher sein, es rsync -av --remove-source-filesals Alias ​​für zu verwenden mv.

sepehr
quelle
4

Der einfachste Weg, dies zu tun, ist:

mkdir [directory name] && mv [filename] $_

Angenommen, ich habe PDF-Dateien heruntergeladen, die sich in meinem Download-Verzeichnis ( ~/download) befinden, und möchte alle in ein nicht vorhandenes Verzeichnis verschieben (sagen wir mal)my_PDF ).

Ich gebe den folgenden Befehl ein (um sicherzustellen, dass mein aktuelles Arbeitsverzeichnis ist ~/download):

mkdir my_PDF && mv *.pdf $_

Sie können eine -pOption hinzufügen , mkdirwenn Sie Unterverzeichnisse wie folgt erstellen möchten: (Angenommen, ich möchte ein Unterverzeichnis mit dem Namen erstellen python):

mkdir -p my_PDF/python && mv *.pdf $_
Abdoul Seibou
quelle
2

Alberner, aber funktionierender Weg:

mkdir -p $2
rmdir $2
mv $1 $2

Erstellen Sie das Verzeichnis mit mkdir -p, einschließlich eines temporären Verzeichnisses, das den Namen der Zieldatei teilt, entfernen Sie dieses Dateinamenverzeichnis mit einem einfachen rmdir und verschieben Sie Ihre Datei an das neue Ziel. Ich denke, die Antwort mit dirname ist wahrscheinlich die beste.

Blarn Blarnson
quelle
Yepp, irgendwie albern. :-) Wenn $ 2 ein Verzeichnis ist (wie in der ursprünglichen Frage), schlägt es kläglich fehl, da das letzte Verzeichnis gelöscht wird und 'mv' fehlschlägt. Und wenn $ 2 bereits vorhanden ist, wird auch versucht, es zu löschen.
Tylla
Vielen Dank! Dies ist genau das, was ich erreichen musste: mv ./old ./subdir/new, wo subdir noch nicht existiert
Mike Murray
2
((cd src-path && tar --remove-files -cf - files-to-move) | ( cd dst-path && tar -xf -))
svaardt
quelle
1

Code:

if [[ -e $1 && ! -e $2 ]]; then
   mkdir --parents --verbose -- "$(dirname -- "$2")"
fi
mv --verbose -- "$1" "$2"

Beispiel:

Argumente: "d1" "d2 / sub"

mkdir: created directory 'd2'
renamed 'd1' -> 'd2/sub'
John Doe
quelle
1

Dies wird foo.cin das neue Verzeichnis bazmit dem übergeordneten Verzeichnis verschoben bar.

mv foo.c `mkdir -p ~/bar/baz/ && echo $_`

Die -pOption zum mkdirErstellen von Zwischenverzeichnissen nach Bedarf.
Ohne -palle Verzeichnisse im Pfad muss das Präfix bereits vorhanden sein.

Alles in Backticks ``wird ausgeführt und die Ausgabe wird als Teil Ihres Befehls inline zurückgegeben.
Da mkdirnichts zurückgegeben wird, wird nur die Ausgabe von echo $_zum Befehl hinzugefügt.

$_verweist das letzte Argument auf den zuvor ausgeführten Befehl.
In diesem Fall wird der Pfad zu Ihrem neuen Verzeichnis ( ~/bar/baz/) zurückgegeben, das an den mkdirBefehl übergeben wurde.


Ich habe ein Archiv entpackt, ohne ein Ziel anzugeben, und wollte alle Dateien außer demo-app.zipmeinem aktuellen Verzeichnis in ein neues Verzeichnis namens verschieben demo-app.
Die folgende Zeile macht den Trick:

mv `ls -A | grep -v demo-app.zip` `mkdir -p demo-app && echo $_`

ls -AGibt alle Dateinamen einschließlich versteckter Dateien zurück (mit Ausnahme der impliziten .und.. ).

Das Pipe-Symbol |wird verwendet, um die Ausgabe des lsBefehls an grep( ein Befehlszeilen-Nur-Text-Suchdienstprogramm) weiterzuleiten .
Das -vFlag weist Sie grepan, alle Dateinamen mit Ausnahme zu suchen und zurückzugeben demo-app.zip.
Diese Liste von Dateien wird unserer Befehlszeile als Quellargumente für den Befehl move hinzugefügt mv. Das Zielargument ist der Pfad zu dem neuen Verzeichnis, an das mit mkdirverwiesen wird $_und mit ausgegeben wird echo.

Evan Wunder
quelle
1
Dies ist eine nette Detail- und Erklärungsstufe - und insbesondere für einen ersten Beitrag. Vielen Dank!
Jeremy Caney
Vielen Dank, @JeremyCaney! Ich freue mich darauf zu helfen!
Evan Wunder
0

Sie können sogar Klammerverlängerungen verwenden:

mkdir -p directory{1..3}/subdirectory{1..3}/subsubdirectory{1..2}      
  • das erstellt 3 Verzeichnisse (Verzeichnis1, Verzeichnis2, Verzeichnis3),
    • und in jedem von ihnen zwei Unterverzeichnisse (Unterverzeichnis1, Unterverzeichnis2),
      • und in jedem von ihnen zwei Unterverzeichnisse (Unterverzeichnis1 und Unterverzeichnis2).

Sie müssen bash 3.0 oder neuer verwenden.

Stefan Gruenwald
quelle
5
Interessant, beantwortet aber die Frage des OP nicht.
Alexey Feldgendler
0
$what=/path/to/file;
$dest=/dest/path;

mkdir -p "$(dirname "$dest")";
mv "$what" "$dest"
cab404
quelle
1
Um es zu einem eigenständigen Skript zu machen, ersetzen Sie einfach $ dest und $ src durch $ 1 und $ 2
cab404
0

Meine One-String-Lösung:

test -d "/home/newdir/" || mkdir -p "/home/newdir/" && mv /home/test.txt /home/newdir/
Andrey
quelle
0

Sie können Befehle verketten: mkdir ~/bar/baz | mv foo.c ~/bar/baz/
oder Shell-Skript ist hier:

#!/bin/bash
mkdir $2 | mv $1 $2

1. Öffnen Sie einen beliebigen Texteditor.
2. Kopieren und Einfügen des Shell-Skripts.
3. Speichern Sie es als mkdir_mv.sh.
4. Geben Sie das Verzeichnis Ihres Skripts ein: cd your/script/directory
5. Ändern Sie den Dateimodus : chmod +x mkdir_mv.sh
6. Legen Sie den Alias ​​fest : alias mv="./mkdir_mv.sh"
Wenn Sie einen Befehl ausführen mv, wird jetzt ein Verschiebungsverzeichnis erstellt, falls es nicht vorhanden ist.

Sharofiddin
quelle
0

Basierend auf einem Kommentar in einer anderen Antwort, hier ist meine Shell-Funktion.

# mvp = move + create parents
function mvp () {
    source="$1"
    target="$2"
    target_dir="$(dirname "$target")"
    mkdir --parents $target_dir; mv $source $target
}

Fügen Sie dies in .bashrc oder ähnliches ein, damit Sie es überall verwenden können.

Ben Winding
quelle