Сначала я сделаю тестовую базу - 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
с содержимым каталога (если он не пустой и не включает файлы / папки с именами, начинающимися с .
) в качестве списка аргументов. 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
символ, если цель не заканчивается на a /slash
, и последний компонент имени пути исходного_файла . Эта вторая форма предполагается, когда последний операнд называет существующий каталог.
Так что, если *
расширяется до: