Was macht "mv ./*" ohne Angabe des Ziels?

27

Ich habe versehentlich vergessen, das Ziel anzugeben, bevor ich die Eingabetaste gedrückt habe. Wohin werden mv ./*ohne Angabe eines Ziels die Dateien und Verzeichnisse unter dem aktuellen Verzeichnis verschoben?

Tim
quelle
4
"Ich bin überrascht, dass mv1 Argument akzeptiert wird." Es akzeptiert alle Argumente, die die Shell nach dem Erweitern des Befehls übergeben hat *.
Glglgl

Antworten:

41

Wenn das letzte Argument ein Verzeichnis war, haben Sie einfach alle Dateien und Verzeichnisse in Ihrem aktuellen Arbeitsverzeichnis (mit Ausnahme derjenigen, deren Namen mit Punkten beginnen) in dieses Verzeichnis verschoben. Wenn zwei Dateien vorhanden waren, hat die erste Datei möglicherweise die zweite Datei überschrieben.

Hier sind einige Demonstrationen:

Mehr als zwei Dateien und das letzte Argument ist eine Datei

$ mkdir d1 d2 d3
$ touch a b c e
$ mv *
mv: target 'e' is not a directory

Mehr als zwei Dateien und das letzte Argument ist ein Verzeichnis

$ mkdir d1 d2 d3
$ touch a b c
$ mv -v *
'a' -> 'd3/a'
'b' -> 'd3/b'
'c' -> 'd3/c'
'd1' -> 'd3/d1'
'd2' -> 'd3/d2'

Zwei Akten

$ touch a b
$ mv -v *
'a' -> 'b'

Weitere Erklärung

Die Shell erweitert das glob ( *) zu Argumenten für mv. Der Glob wird normalerweise in alphabetischer Reihenfolge erweitert. mvsieht immer eine Liste von Dateien und Verzeichnissen. Es sieht nie den Globus selbst.

Der Befehl mvunterstützt zwei Arten des Verschiebens. Eins ist mv file ... directory. Der andere ist mv old-file-name new-file-name(oder mv old-file-name directory/new-file-name).

terdon
quelle
30

Zuerst mache ich eine Testbasis - 5 Dateien und einen Ordner:

touch file1 file2 file3 file4 file5
mkdir folder

Als nächstes führe ich einen Testbefehl aus. Die -vOption gibt an, dass jeder Befehl, den die Shell ausführt, gedruckt werden soll stderr. Die -xOption gibt an, dass dasselbe gedruckt werden soll stderr- dies soll jedoch erfolgen, nachdem der Befehl ausgewertet wurde, aber bevor die Shell ihn ausführt.

sh -cxv 'echo mv *'

AUSGABE

echo mv *
+ echo mv file1 file2 file3 file4 file5 folder
mv file1 file2 file3 file4 file5 folder

Sie sehen also, dass der Befehl, den ich der Shell übergebe, echo mv *und der Befehl, den die Shell ausführt, nachdem sie * erweitert wurde, echo mvvon all diesen Dateien und dem Ordner gefolgt wird.

Standardmäßig erweitert die Shell Globs wie folgt :

sh -cxv 'echo file[1-5]'

AUSGABE

echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5

Dies ist ein Ergebnis der set [+-]fGlob-Funktion:

sh -cxvf 'echo file[1-5]'

AUSGABE

echo file[1-5]
+ echo 'file[1-5]'
file[1-5]

Wenn Sie also einen Befehl in einer Shell ausführen, die mit Standardoptionen wie mv *der Shell konfiguriert ist, wird *eine Argumentliste aller Dateien im aktuellen Verzeichnis in das Wort eingeblendet, sortiert nach Ländereinstellung. Es führt den Syscall exec(ve)für mv (im Wesentlichen) mit dieser angehängten Argumentliste aus. mvErhält also alle Argumente, während die Shell sie sortiert. Neben stracediesen Effekten können Sie das Debug-Out wie folgt erneut verwenden:

sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT

AUSGABE

sh\000-s\000--\000mv\000file1\000file2\000file3\000file4\000file5\000folder\
\000$
mv file1 file2 file3 file4 file5 folder

Und portabel:

( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1

AUSGABE

: /mv/file1/file2/file3/file4/file5/folder/

Grundsätzlich wird die Shell mvmit dem Inhalt des Verzeichnisses (wenn es nicht leer ist und keine Dateien / Ordner enthält, deren Namen mit "" beginnen .) als Argumentliste ausgeführt. mvwird POSIX angegeben, um sein letztes Argument als Verzeichnis zu interpretieren, wenn es mit mehr als zwei Argumenten aufgerufen wird - auf dieselbe Weise lnwie (weil es sich in der zugrunde liegenden Funktion tatsächlich um unglaublich ähnliche Tools handelt) .

Genug davon echo:

sh -cxv 'mv *' ; ls

AUSGABE

mv *
+ mv file1 file2 file3 file4 file5 folder
folder/

Alle Dateien wurden in das letzte Argument verschoben, da es sich um einen Ordner handelt. Was ist nun, wenn es sich nicht um einen Ordner handelt?

sh -cxv 'cd *; mv *'; ls . *

AUSGABE

cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory

.:
folder/

folder:
file1  file2  file3  file4  file5

So sollte sich POSIX mv in diesem Fall verhalten:

mv [-if] source_file target_file

mv [-if] source_file... target_dir

In der ersten Synopse verschiebt das mvDienstprogramm die vom Operanden source_file angegebene Datei in das von target_file angegebene Ziel . Diese erste Zusammenfassungsform wird angenommen, wenn der letzte Operand kein vorhandenes Verzeichnis benennt und kein symbolischer Link ist, der auf ein vorhandenes Verzeichnis verweist. Wenn in diesem Fall Quellendateinamen einer Nicht-Verzeichnisdatei und Zieldatei mit einem nachgestellten /slashZeichen enden , mvwird dies als Fehler behandelt, und es werden keine Quellendatei- Operanden verarbeitet.

mvVerschiebt in der zweiten Zusammenfassungsform jede Datei, die von einem Quellendatei- Operanden benannt wurde, in eine Zieldatei im vorhandenen Verzeichnis, das vom Operanden Zielverzeichnis benannt wurde , oder verweist darauf, wenn Zielverzeichnis eine symbolische Verknüpfung ist, die auf ein vorhandenes Verzeichnis verweist. Der Zielpfad für jede Quellendatei besteht aus der Verkettung des Zielverzeichnisses, einem einzelnen /slashZeichen, wenn das Ziel nicht auf a endete /slash, und der letzten Pfadname-Komponente der Quellendatei . Diese zweite Form wird angenommen, wenn der letzte Operand ein vorhandenes Verzeichnis benennt.

Also, wenn *erweitert um:

  • zwei Dateien

    • Sie sollten nur eine Datei haben, die von der ersten renamedzur zweiten nach der zweiten gehört unlinked.
  • Eine oder mehrere Dateien, gefolgt von einem Verzeichnis oder einem Link zu einer

    • Sie sollten nur ein Verzeichnis oder einen Link zu einem haben, in das der gesamte vorherige Inhalt der Eltern gerade verschoben wurde.
  • noch etwas

    • Sie sollten eine Fehlermeldung und einen befriedigenden Seufzer der Erleichterung haben.
mikeserv
quelle
1
Ja, mit dem alten shDing habe ich das Debuggen vergessen, aber in Bezug auf meine ursprüngliche Frage bedeutet es, dass das Problem nicht die Shell ist mv? Das ist böse, es ist einfacher als ich ursprünglich dachte, aber es ist eine echte Falle.
user2485710
5
@ user2485710, der entscheidende Punkt ist, dass in Unix die Shell für das Erweitern von Platzhaltern verantwortlich ist, bevor die Befehlszeilenargumente an den Befehl übergeben werden. In Windows muss jeder Befehl Platzhalter selbst erweitern. Auf diese Weise können Unix-Shells erweiterte Platzhalterfunktionen anbieten, die mit jedem Befehl funktionieren.
cjm
2
@mikeserv Angesichts der Tatsache, dass diese Dateien nicht so wichtig waren, habe ich mich beeilt, aufzuräumen und mit dem nächsten Schritt fortzufahren, aber ich kann die Dinge bestätigen, wie sie jetzt in meiner Bearbeitung meines ersten Posts / meiner ersten Frage erscheinen.
user2485710
6
@mikeserv tl; dr, *ist das nicht ein einziges argument? Recht? :)
Bernhard
1
@Bernhard - das ist der einzige Fall, den ich nicht behandelt habe - weil es sein könnte .
mikeserv
18

Zuerst wird die Shell ./*auf alle Dateien im aktuellen Verzeichnis erweitert (außer Dateien, die mit einem Punkt beginnen).

  • Wenn es keine oder nur eine Datei gibt: mvschlägt fehl
  • wenn es zwei dateien gibt: die erste wird in die zweite verschoben (die gehen also verloren)
  • Wenn es mehr als zwei Dateien gibt:
    • Wenn das letzte Verzeichnis ein Verzeichnis ist: Alle Dateien werden in dieses Verzeichnis verschoben
    • sonst mvscheitert.
Jofel
quelle
1
Vielen Dank. Wie kann man feststellen, welches Unterverzeichnis "das letzte" ist?
Tim
7
Rufen Sie echo ./*einfach an und sehen Sie, welche Reihenfolge Ihre Shell verwendet (normalerweise alphabetisch).
Jofel
Wenn es mit einer Datei fehlschlägt, warum sagt es das nicht?
Noumenon
@Noumenon Ich verstehe deinen Kommentar nicht. mvGibt eine Fehlermeldung zurück, wenn sie nur mit einem Argument aufgerufen wird.
Jofel
Du hast recht. Das gleiche Kommando aus meiner Geschichte gibt missing destination file operand after *filename*es jetzt , obwohl es gestern nicht geschah.
Noumenon
4

Bei der Eingabe mv ./*wird Ihre Shell ./*vor der Ausführung erweitert mv.

Ein paar Dinge zu beachten:

  • Wenn ./*in weniger als 2 Argumente erweitert mvwird, wird logischerweise ein Fehler erzeugt.
  • ./* wird normalerweise in jede Datei (einschließlich Verzeichnis) erweitert, die im aktuellen Verzeichnis vorhanden ist und nicht mit einem Punkt beginnt.
  • Sie können steuern, in was ./*erweitert wird, indem Sie die Dokumentation Ihrer Shell lesen ( man 7 globist ein Einstiegspunkt in das Thema). Unterschiedliche Shells haben unterschiedliche Optionen.
rahmu
quelle
1

Was macht mv *das?

Hier ist eine kürzere Antwort:

Die Shell erweitert den Platzhalter *zu einer Liste mit Verzeichnisinhalten. Dann übergibt die Shell diese vollständige Liste an den Befehl. Der Befehl sieht nie *.

Der Befehl mv file1 file2 ... filen directoryverschiebt file1 ... filen in das Verzeichnis.

Beispiel

Hier erstelle ich ein Testverzeichnis mit drei Dateien

$ mkdir t
$ cd t
$ echo a>a; echo b>b; echo c>c
$ ls
a  b  c

Sie können nicht mehrere Dateien in eine einzige Datei verschieben

$ mv *
mv: target `c' is not a directory

Fügen wir ein Unterverzeichnis hinzu

$ mkdir d

Sie können mehrere Dateien in ein Unterverzeichnis verschieben

$ mv *
$ ls
d
$ ls d
a  b  c
RedGrittyBrick
quelle
Es mv file1 file2 ... filen directoryist sehr unwahrscheinlich, dass der Befehl überhaupt etwas damit zu tun hat *.
mikeserv
@ Mike: Meine Antwort weist darauf hin, dass die letzte Stufe der Shell-Erweiterung letztere effektiv in erstere umwandelt. Nicht zu wissen, scheint die Ursache für die Verwirrung des OP zu sein.
RedGrittyBrick
Ja, aber ich gehe davon aus, dass die Shell nicht erweitert *werden würde, es file dirsei denn, es gibt ein Gebietsschema, in dem dFolgendes folgt f.
mikeserv
@mikeserv: Es gibt so ein Gebietsschema, mein Beispiel ist ein Ausschneiden und Einfügen von Putty, das eine Verbindung zu einem CentOS 5.6 GNU / Linux-System herstellt.
RedGrittyBrick
welches Gebietsschema ist das? Dein Beispiel ist a b c ddas, was nicht ist file ... dir. Ich habe überhaupt nur kommentiert, weil Sie die Sorte nirgendwo erwähnen und sagen, nur das *wird file ... dirwas nicht passiert.
mikeserv