С mv
cannot move 'foo' to a subdirectory of itself
в этом случае безвреден, вы можете игнорировать его. Я имею в виду, что mv
переместит остальных, несмотря на предупреждение. Однако статус выхода не будет равен 0
, это может быть серьезным препятствием в сценариях, где вы хотите прервать или предпринять корректирующее действие, если mv
не удается.
Об этом уже говорилось в комментарии: вы можете отключить mv
, перенаправив stderr с помощью 2>/dev/null
; другие предупреждения или ошибки также будут перенаправлены.
Я думаю, что вы не можете легко «уточнить шаблон, чтобы ограничить источник файлами, а не каталогами». Тем не менее вы можете использовать подход, который не соответствует буквальному foo
. Следующий подход не имеет ничего общего с файлами или каталогами; только с именами, потому что глобусы имеют дело с именами; так что в некоторых случаях этого может быть недостаточно.
*foo*
соответствует четырем дизъюнктивным типам объектов:
- сам
foo
- foo только с префиксом, как
bar-foo
- вы можете сопоставить их с помощью *?foo
- foo с префиксом и постфиксом, например
bar-foo-baz
- *?foo?*
- foo только с постфиксом, как
foo-baz
- foo?*
Чтобы исключить foo
вам нужны только последние три (2-4), поэтому первый подход может быть следующим:
mv *?foo *?foo?* foo?* foo/
Если у вас не установлена опция оболочки nullglob
, любой шаблон должен соответствовать чему-либо, иначе он будет передан в mv
буквально. Ты не хочешь этого. Решение состоит в том, чтобы заранее выполнить следующую команду:
shopt -s nullglob
Эта опция оболочки заставит любой непревзойденный шар расширяться до нуля. Я советую вам также изучить вариант dotglob
.
Обратите внимание, что *?foo*
соответствует (2-3). Точно так же *foo?*
спички (3-4). Дополнительно учтите --
сообщить mv
что все следующие аргументы не являются опциями. Таким образом, файл, подобный --foo
, не будет интерпретироваться как (неизвестный) параметр. Это приводит к двум лучшим командам:
mv -- *?foo* foo?* foo/
или же
mv -- *?foo *foo?* foo/
Неважно, какой вы выберете. Просто не используйте mv *?foo* *foo?* foo/
; он передаст (например, bar-foo-baz
в mv
дважды (то есть как два отдельных аргумента), что приведет к ошибке, когда инструмент попытается переместить этот объект во второй раз.
Когда совпадение не найдено, мои команды mv
вырождаются в mv foo/
и выдают ошибку. Ваша оригинальная команда mv
(с nullglob
) получит литерал *foo*
и выдаст еще одну ошибку.
Пример сеанса консоли:
$ shopt -s nullglob
$ mkdir foo
$ touch bar-foo bar-foo-baz foo-baz xyfooz.tex ./--foo
$ mv -v -- *?foo* foo?* foo/
'bar-foo' -> 'foo/bar-foo'
'bar-foo-baz' -> 'foo/bar-foo-baz'
'--foo' -> 'foo/--foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'foo-baz' -> 'foo/foo-baz'
$
Простой взлом
Допустим, dummy
не существует.
mv foo dummy
mv *foo* dummy/
mv dummy foo
Переименование каталога должно быть очень быстрым в файловых системах на основе inode. Программы, имеющие файлы из foo/
уже открытые, не должны ни мешать, ни ломаться, потому что важен именно inode. Однако если какой-либо программе нужно открыть файл (по пути, включая foo/
) между первым и третьим mv
, это не удастся. Могут возникнуть другие побочные эффекты (вообразите стороннюю программу, воссоздающую foo/
в то же время), поэтому я называю этот подход хаком. Подумайте дважды, прежде чем использовать его.
В любом случае, find
Различение файлов из каталогов - это работа для find
. То, что вы сделали с find
в основном верный путь. То, что вы хотите сделать (и то, что я делал выше) с помощью mv
и shell-шаров, кажется… ну, в большинстве случаев "менее правильным". Это ваша команда:
find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
Эта find
будет запускать отдельную mv
для каждого соответствующего файла, вся команда будет работать плохо. По этой причине можно захотеть переключиться на один mv
. Если это ваша точка зрения (и ваши mv
и find
достаточно богаты), то вам следует рассмотреть этот "очень правильный" способ:
find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +
Преимущества перед вашей командой find
и / или над простым mv
с glob(s):
- один
mv
может получить несколько файлов для работы;
- тем не менее, если для одной командной строки слишком много файлов для обработки,
find
запускает дополнительный процесс (ы) mv
;
- в случае, когда ни один файл не соответствует шаблону,
mv
вообще не будет запускаться;
- благодаря
--
такой файл, как --foo
, не будет интерпретироваться как (неизвестная) опция для mv
;