Що робить `mv ./*` без зазначення призначення?


27

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



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функції глобуса:

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

ВИХІД

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

Таким чином, коли ви запускаєте команду в оболонці, налаштованій за замовчуванням, наприклад mv *оболонка розширює в *слово список аргументів всіх файлів у поточному каталозі, відсортованих за місцевістю. Він робить syscall 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зі вмістом каталогу (якщо він не порожній і не включає файли / папки з іменами, що починаються з .) в якості списку аргументів. mvє POSIX вказано інтерпретувати його останній аргумент як каталог , якщо він викликається з більш ніж двома аргументами - так само , як 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символ , якщо мета не закінчується на /slash, а останній шляху компонента ісходний_файл . Ця друга форма передбачається, коли кінцевий операнд називає існуючий каталог.

Тож якщо *розширюється на:

  • два файли

    • У вас повинен бути лише один файл, який є першим renamedдо другого після другого unlinked.
  • один або декілька файлів, за якими останній знаходиться каталог або посилання на один

    • У вас повинен бути лише один каталог або посилання на один, куди було переміщено весь попередній вміст його батьків.
  • щось ще

    • У вас повинно з’явитися повідомлення про помилку і задоволення зітхнути з полегшенням.

1
так, використовуючи стару shріч, я забув про налагодження з цим, але щодо мого оригінального питання це означає, що проблема не в оболонці mv? Це противно, простіше, ніж я спочатку думав, але це справжня пастка.
user2485710

5
@ user2485710, ключовим моментом є те, що в Unix оболонка відповідає за розширення символів перед передачею аргументів командного рядка команді. У Windows кожна команда повинна сама розширювати підстановки. Це дозволяє оболонкам Unix пропонувати розширені засоби wildcard, які працюють з будь-якою командою.
cjm

2
@mikeserv з огляду на той факт, що ці файли не такі важливі, я кинувся на прибирання та перейшов до наступного кроку, але можу підтвердити речі, як вони тепер з’являються у моїй редакції мого першого повідомлення / питання.
user2485710

6
@mikeserv tl; dr, *це не єдиний аргумент? Правильно? :)
Бернхард

1
@ Бернхард - насправді це єдиний випадок, якого я не висвітлював - тому що це може бути.
mikeserv

18

Спочатку оболонка розширюється ./*на всі файли в поточному каталозі (крім файлів, що починаються з крапки).

  • якщо немає або є лише один файл: mvне вдалося
  • якщо є два файли: перший переміщується у другий (який, отже, втрачається)
  • якщо файлів більше двох:
    • якщо останній - це каталог: всі файли переміщуються в цю каталог
    • інакше mvвиходить з ладу.

1
Спасибі. як сказати, який підкаталог "останній"?
Тім

7
Просто зателефонуйте echo ./*, подивіться, який порядок використовує ваша оболонка (як правило, в алфавітному порядку).
jofel

Якщо він не вдається з одним файлом, чому він не каже так?
Номенон

@Noumenon Я не розумію твого коментаря. mvповертає повідомлення про помилку, якщо воно викликається лише одним аргументом.
jofel

Ти правий. Та сама команда з моєї історії тепер дає, missing destination file operand after *filename*хоча вчора цього не зробили.
Номенон

4

Під час введення mv ./*оболонка розгорнеться ./*перед виконанням mv.

Кілька речей, які слід зазначити:

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

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

Команда 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.