Что делает `mv ./*` без указания места назначения?

27

Я случайно забыл указать пункт назначения перед нажатием клавиши Return. Куда mv ./*без указания места назначения перемещать файлы и каталоги в текущем каталоге?

Тим
источник
1
Связано: unix.stackexchange.com/questions/79882/…
lgeorget
4
«Я удивлен, что mvпринимает 1 аргумент» Это не так. Он принимает все аргументы, которые ему передала оболочка после расширения *.
glglgl

Ответы:

41

Если последним аргументом был каталог, вы просто переместили все файлы и каталоги в текущем рабочем каталоге (кроме тех, чьи имена начинаются с точек) в этот каталог. Если было два файла, первый файл мог перезаписать второй файл.

Вот несколько демонстраций:

Более двух файлов и последний аргумент является файлом

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

Более двух файлов и последний аргумент - это каталог

$ 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'

Два файла

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

Дальнейшее объяснение

Оболочка расширяет glob ( *) в аргументы для mv. Глобус обычно расширяется в алфавитном порядке. mvвсегда видит список файлов и каталогов. Он никогда не видит сам шар.

Команда mvподдерживает два типа перемещения. Один есть mv file ... directory. Другой mv old-file-name new-file-name(или mv old-file-name directory/new-file-name).

Тердон
источник
30

Сначала я сделаю тестовую базу - 5 файлов и одна папка:

touch file1 file2 file3 file4 file5
mkdir folder

Далее я выполнил тестовую команду. В -vпараметр указывает , что я хочу каждую команду оболочка выполняет быть распечатана в stderr. В -xпараметр указывает , что я хочу того же печатается stderr- но я хочу сделать это после того, как команда вычисляется , но прежде , чем оболочка запускает его.

sh -cxv 'echo mv *'

ВЫХОД

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

Итак, вы видите, что команда, которую я передаю оболочке, echo mv *и команда, которую оболочка выполняет после * развертывания, echo mvсопровождаются всеми этими файлами и папкой.

По умолчанию оболочка будет расширять глобусы, такие как:

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

ВЫХОД

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

Это результат set [+-]fфункции glob:

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

ВЫХОД

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

Поэтому, когда вы запускаете команду в оболочке, настроенной с параметрами по умолчанию, такими mv *как оболочка, расширяет в *слове список аргументов всех файлов в текущем каталоге, отсортированных по локали. Он выполняет системный вызов exec(ve)для mv (по существу) с добавленным списком аргументов. Таким образом, mvполучает все аргументы, поскольку оболочка обрабатывает их и сортирует их. Помимо выполненияstrace чтобы увидеть эти эффекты, вы можете снова использовать отладку, например:

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

ВЫХОД

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

И переносимо:

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

ВЫХОД

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

По сути, оболочка выполняется mvс содержимым каталога (если он не пустой и не включает файлы / папки с именами, начинающимися с .) в качестве списка аргументов. mvPOSIX указывается, чтобы интерпретировать свой последний аргумент как каталог, если он вызывается с более чем двумя аргументами - таким же образомln это (потому что, на самом деле, они невероятно аналогичные инструменты в основной функции) .

Достаточно, echoхотя:

sh -cxv 'mv *' ; ls

ВЫХОД

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

Все файлы были перемещены в последний аргумент - потому что это папка. А что если это не папка?

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

ВЫХОД

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

.:
folder/

folder:
file1  file2  file3  file4  file5

Вот как POSIX mv должен вести себя в этом случае:

mv [-if] source_file target_file

mv [-if] source_file... target_dir

В первой форме резюме mvутилита должна переместить файл, названный операндом source_file, в место назначения, указанное в файле target_file . Эта первая форма краткого обзора предполагается, когда последний операнд не называет существующий каталог и не является символической ссылкой, ссылающейся на существующий каталог. В этом случае, если source_file присваивает имя файлу , не являющемуся каталогом, а target_file заканчивается завершающим /slashсимволом,mv следует рассматривать как ошибку, и операнды source_file обрабатываться не будут.

Во второй форме mvкраткого обзора каждый файл, названный операндом source_file , должен перемещаться в целевой файл в существующем каталоге, названный операндом target_dir , или на который ссылается, если target_dir является символической ссылкой, ссылающейся на существующий каталог. Путь назначения для каждого исходного_файла должен представлять собой конкатенацию целевого каталога, один /slashсимвол, если цель не заканчивается на a /slash, и последний компонент имени пути исходного_файла . Эта вторая форма предполагается, когда последний операнд называет существующий каталог.

Так что, если *расширяется до:

  • два файла

    • У вас должен быть только один файл, с первого renamedпо второй после второго unlinked.
  • один или несколько файлов, за которыми следует каталог или ссылка на него

    • У вас должен быть только один каталог или ссылка на него, куда только что было перемещено все предыдущее содержимое его родителя.
  • что-нибудь еще

    • У вас должно быть сообщение об ошибке и удовлетворительный вздох облегчения.
mikeserv
источник
1
да, используя старую shштуку, я забыл об отладке с этим, но относительно моего первоначального вопроса, это означает, что проблема не в оболочке mv? Это противно, это проще, чем я думал, но это настоящая ловушка.
user2485710
5
@ user2485710, ключевым моментом является то, что в Unix оболочка отвечает за расширение подстановочных знаков перед передачей аргументов командной строки команде. В Windows каждая команда должна раскрывать шаблоны самостоятельно. Это позволяет оболочкам Unix предлагать расширенные подстановочные знаки, которые работают с любой командой.
CJM
2
@mikeserv, учитывая тот факт, что эти файлы были не так важны, я поспешил привести себя в порядок и перейти к следующему шагу, но я могу подтвердить вещи, как они теперь появляются в моем редактировании моего первого поста / вопроса.
user2485710
6
@mikeserv tl; доктор, *это не единственный аргумент? Правильно? :)
Бернхард
1
@ Бернхард - на самом деле, это один случай, который я не раскрыл - потому что это может быть.
mikeserv
18

Сначала оболочка распространяется ./*на все файлы в текущем каталоге (кроме файлов, начинающихся с точки).

  • если нет или только один файл: mvтерпит неудачу
  • если есть два файла: первый перемещается во второй (который, следовательно, теряется)
  • если есть более двух файлов:
    • если последний является каталогом: все файлы перемещаются в этот каталог
    • в противном случае mvне удается.
jofel
источник
1
Спасибо. Как определить, какой подкаталог является «последним»?
Тим
7
Просто позвоните echo ./*и посмотрите, какой порядок использует ваша оболочка (обычно в алфавитном порядке).
Джофель
Если это терпит неудачу с одним файлом, почему это не говорит так?
Ноумен
@ Нумен, я не понимаю ваш комментарий. mvвозвращает сообщение об ошибке, если оно вызывается только с одним аргументом.
17
Вы правы. Та же самая команда из моей истории теперь дает, missing destination file operand after *filename*хотя вчера это не так.
Ноумен
4

Когда вы печатаете mv ./*, ваша оболочка раскроется ./*перед выполнениемmv .

Несколько вещей на заметку:

  • Если ./*развернуть менее чем в 2 аргумента mv, логически выдаст ошибку.
  • ./* обычно раскрывается в каждый файл (включая каталог), присутствующий в текущем каталоге и не начинающийся с точки.
  • Вы можете контролировать то, что ./*расширяется, читая документацию вашей оболочки ( man 7 globэто точка входа в тему). У разных оболочек будут разные варианты.
rahmu
источник
1

Что делает mv *?

Вот короткий ответ:

Оболочка расширяет подстановочный знак *до списка содержимого каталога. Затем оболочка передает этот полный список команде. Команда никогда не видит *.

Команда mv file1 file2 ... filen directoryпереместит file1 ... filen в каталог.

пример

Здесь я делаю тестовый каталог, содержащий три файла

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

Вы не можете переместить несколько файлов в один файл

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

Давайте добавим подкаталог

$ mkdir d

Вы можете переместить несколько файлов в подкаталог

$ mv *
$ ls
d
$ ls d
a  b  c
RedGrittyBrick
источник
Команда, mv file1 file2 ... filen directoryскорее всего, не имеет ничего общего с *.
mikeserv
@Mike: Мой ответ указывает на то, что последний этап расширения оболочки эффективно превращает последний в первый. Не зная этого, кажется, корень путаницы ОП.
RedGrittyBrick
да, но моя точка оболочка не будет расширяться *в , file dirесли нет какой - то локали , в котором dследует f.
mikeserv
@mikeserv: есть такая локаль, мой пример - это вырезка и вставка из Putty для подключения к системе CentOS 5.6 GNU / Linux.
RedGrittyBrick
какая локаль это? Ваш пример, a b c dкоторый не file ... dir. Я только прокомментировал, потому что вы нигде не упоминаете о сортах и ​​говорите только то, что *становится, file ... dirчего не происходит.
mikeserv