перемещение файлов, соответствующих шаблону, который захватывает каталог

перемещение файлов, соответствующих шаблону, который захватывает каталог

Учитывать:

$ 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*соответствует четырем дизъюнктивным типам объектов:

  1. fooсам
  2. foo только с префиксом, например bar-foo– вы можете сопоставить их по*?foo
  3. foo с префиксом и постфиксом, например bar-foo-baz*?foo?*
  4. foo только с постфиксом, например foo-bazfoo?*

Для исключения 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команда (с nullglobunset) получит литерал *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;

Связанный контент