С 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 ;