mv: Verschiebt die Datei nur, wenn das Ziel nicht existiert

44

Kann ich es so verwenden mv file1 file2, dass es nur dort ankommt file1, file2wo file2es nicht existiert?

ich habe es versucht

yes n | mv -i file1 file2

(Hier können Sie mvfragen, ob file2 überschrieben werden soll, und automatisch mit no antworten), aber neben dem Missbrauch werden -imir auch keine netten Fehlercodes angezeigt (immer 141 anstelle von 0, wenn verschoben, und etwas anderes, wenn nicht verschoben).

Fabian Schmitthenner
quelle
3
Sie müssen die pipefailOption aktiviert haben, da 141 der Exit-Status von ist yes, nicht mvder Grund, hier eine SIGPIPE zu erhalten.
Stéphane Chazelas
Dieser Ansatz schlägt auch fehl, wenn Datei2 ein Verzeichnis ist (dabei wird Datei1 in das Verzeichnis Datei2 verschoben). GNU MV hat eine -Tdafür.
Stéphane Chazelas
@ StéphaneChazelas Wenn der Wunsch besteht, den Ausgangsstatus von mvanstelle des von zu verwenden yes, ist die einfachste Lösung möglicherweisemv -i file1 file2 < <(yes n)
kasperd

Antworten:

63

mv -vn file1 file2. Dieser Befehl wird tun, was Sie wollen. Sie können überspringen, -vwenn Sie möchten.

-v macht es wortreich - mv sagt dir, dass es eine Datei verschoben hat, wenn es diese verschiebt (nützlich, da die Möglichkeit besteht, dass die Datei nicht verschoben wird)

-n Verschiebt sich nur, wenn file2 nicht existiert.

Bitte beachten Sie jedoch, dass dies nicht POSIX ist, wie von ThomasDickey erwähnt .

MatthewRock
quelle
2
Es ist jedoch nicht POSIX .
Thomas Dickey
1
@ThomasDickey unterstützt POSIX dies überhaupt auf atomare Weise?
Fabian Schmitthenner
3
zu @Fabian: Wahrscheinlich nicht, aber selbst innerhalb der vorgeschlagenen Antworten gibt es die Möglichkeit eines Rennens innerhalb der Tools, abhängig davon, wie sie geschrieben wurden.
Thomas Dickey
3
Dies scheint nicht stracerennfrei zu sein. Dies zeigt, dass (auf meinem System) Folgendes verwendet wird: stat ("file2", 0x7ffe3e705d10) = -1 ENOENT (Keine solche Datei oder kein solches Verzeichnis) st_size = 0, ...}) = 0 lstat ("file2", 0x7ffe3e705a10) = -1 ENOENT (Keine solche Datei oder kein solches Verzeichnis) rename ("file1", "file2") = 0 lseek (0, 0, SEEK_CUR) = -1 ESPIPE (unzulässige Suche). Also scheint Umbenennen verwendet zu werden. @ StéphaneChazelas Lösung scheint die richtige zu sein, wenn Sie es wirklich rennfrei machen wollen.
Fabian Schmitthenner
2
Ich frage mich, warum es nicht funktioniertrenameat2
Fabian Schmitthenner
16

mv -n

Von man mveinem GNU-System aus:

-n, --no-clobber
überschreibt keine vorhandene Datei

Auf einem FreeBSD-System:

-nÜberschreiben Sie keine vorhandene Datei. (Die Option -n überschreibt alle vorherigen Optionen -f oder -i.)

Dani_l
quelle
10
if [ ! -e file2 ] && [ ! -L file2 ]
then
    mv file1 file2
# else echo >&2 there is already a file2 file.
fi

Oder:

if ! ls -d file2 > /dev/null 2>&1
then
    mv file1 file2
fi

Würde nur laufen, mvwenn file2es nicht gibt. Beachten Sie, dass es nicht garantiert , dass ein file2nicht außer Kraft gesetzt werden, weil ein file2zwischen dem Test und der entstanden sein könnte mv, aber zur Kenntnis , dass zumindest die aktuellen Versionen von GNU mvmit -ioder -ngeben Sie nicht diese Garantie entweder (obwohl die Race - Bedingung schmale da erfolgt die überprüfung innerhalb von mv).

Auf der anderen Seite ist es portabel, ermöglicht die Unterscheidung zwischen den Fällen und funktioniert unabhängig vom file2Dateityp (regulär, Pipe, sogar Verzeichnis ).

Majenko
quelle
3
Führt dies zu einer Race Condition, bei der eine Datei zwischen der Existenzprüfung und dem Umzug geschrieben werden könnte?
Fabian Schmitthenner
3
Immer eine Möglichkeit, was immer Sie tun.
Majenko
3
Linux-API hat, renameat2die Sie ein RENAME_NOREPLACEFlag geben können . Ich glaube, dies prüft atomar die Existenz der Datei und verschiebt dann die Datei.
Fabian Schmitthenner
-d für Verzeichnisse oder -l für Links oder sogar -e für jeden Dateityp
Majenko
Die Umbenennung kann rennfrei sein, der Rest des mv-Befehls jedoch nicht. Wenn es denkt, dass es keine Verknüpfung lösen muss, schlägt die Umbenennung plötzlich fehl, und es würde (sollte) ein Fehler auftreten.
Majenko
8

Ein rennfreien Ansatz mit GNU lnbereitgestellt file1ist nicht vom Typ Verzeichnis :

ln -PT file1 file2 && rm file1

(Mit Ausnahme von Fehlern in einigen Netzwerk-Dateisystemen), die sicherstellen, dass keine file2Datei überschrieben wird (oder dass, wenn sie file2vom Typ Verzeichnis ist, file1nicht in dieses Verzeichnis verschoben wird), da der link()Systemaufruf im Gegensatz zum rename()Systemaufruf fehlschlägt, wenn die Ziel existiert.

Es wird jedoch einen Zwischenzustand geben, in dem die Datei sowohl als file1als auch existiert file2.

Die -TOption (immer ein link("file1", "file2")Even-If- file2Verzeichnis vom Typ zu erstellen) ist GNU-spezifisch.

Sie können auch den folgenden linkBefehl verwenden:

link file1 file2 && rm file1

Wenn file1es sich jedoch um einen Symlink handelt, handelt es sich abhängig von der Implementierung file2entweder um einen Hardlink zu diesem Symlink oder zum Ziel dieses Symlinks (unter Solaris verwenden /usr/sbin/link, nicht /usr/xpg4/bin/link).

Stéphane Chazelas
quelle
2
Weißt du, ob die Linux-API renameat2mit Flagge RENAME_NOREPLACEatomar ist?
Fabian Schmitthenner
1
@ Fabian, AFAICT ist dafür gedacht, aber es ist sehr neu und wird nicht für alle Dateisysteme unterstützt. In Zukunft können wir davon ausgehen, dass zukünftige MV-Implementierungen unter Linux dies nutzen werden. Dafür wurde es entwickelt.
Stéphane Chazelas
0

Sie können auch angeben, test -e namewelche Variable true zurückgibt, wenn der Name vorhanden ist (unabhängig von Datei, Verzeichnis oder Symlink).

Zum Beispiel:

touch file
mkdir dir
ln -s file symlink
test -e file && echo file exists
test -e dir && echo dir exists
test -e symlink && echo symlink exists
test -e file || echo you wont see this echo
test -e doesnotexist || echo doesnotexist does not exist...
H Briceno
quelle
1
Aber ln -s doesnotexist exists; test -e exists || echo "does it really not exist?". Dasselbe gilt zum Beispiel für ln -s /var/spool/cron/crontabs/. exists(und Sie sind kein Root oder Mitglied der Crontab-Gruppe).
Stéphane Chazelas