Учитывать:
$ ls
foo xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex
Я пытаюсь добиться той же последовательности инструкций, только с помощью mv, а не find:
$ ls
foo xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex
Дополнительно (редактировать),
$ ls -F
foo/ xyfooz.tex*
$ mv -v *foo* foo
mv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
Как убедиться, что предупреждение 'stat' не появляется. Другими словами, я думаю, я бы уточнил шаблон, чтобы ограничить источник файлами, а не каталогами?
GNU bash, 4.3.48(1)-релиз (x86_64-pc-linux-gnu)
$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"
решение1
Сmv
cannot move 'foo' to a subdirectory of itself
в этом случае безвреден, вы можете его игнорировать. Я имею в виду, что mv
остальная часть будет перемещена, несмотря на предупреждение. Однако статус выхода не будет 0
, это может стать серьезным препятствием в сценариях, где вы хотите прерваться или предпринять корректирующее действие, если mv
не удалось.
Это уже былосказал в комментарии: вы можете отключить mv
вывод stderr, перенаправив его с помощью 2>/dev/null
; другие предупреждения и ошибки также будут перенаправлены.
Я думаю, что вы не можете легко "усовершенствовать шаблон, чтобы ограничить источник файлами, а не каталогами". Тем не менее, вы можете использовать подход, который не соответствует литералу foo
. Следующий подход не имеет ничего общего с файлами или каталогами; только с именами, поскольку globs имеют дело с именами; поэтому в некоторых случаях этого может быть недостаточно.
Глоб *foo*
соответствует четырем дизъюнктивным типам объектов:
foo
сам- foo только с префиксом, например
bar-foo
– вы можете сопоставить их по*?foo
- foo с префиксом и постфиксом, например
bar-foo-baz
–*?foo?*
- foo только с постфиксом, например
foo-baz
–foo?*
Для исключения foo
вам понадобятся только последние три (2-4), поэтому первый подход может быть таким:
mv *?foo *?foo?* foo?* foo/
Если у вас не nullglob
установлена опция shell, любой шаблон должен соответствовать чему-то, в противном случае он будет передан 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
unset) получит литерал *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
и подстановки оболочки, кажется... ну, «менее правильным» в лучшем случае. Вот ваша команда:
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
он вообще не будет запущен; - благодаря
--
файлу like--foo
не будет интерпретироваться как (неизвестная) опцияmv
;