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

471
Erwann

Рассматривать:

$ ls foo xyfooz.tex $ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \; $ ls ./foo* xyfooz.tex 

Я пытаюсь добиться той же последовательности инструкций только с помощью mv, а не найти:

$ 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)" 
0
Вы можете отключить `mv`, перенаправив stderr с помощью` 2> / dev / null`. Команда `find` лучше подходит для того, что вы пытаетесь сделать. dsstorefile1 6 лет назад 0
Непонятно, что происходит, но расширение оболочки не может быть ограничено файлами: вам нужно использовать `find` или` для f в * foo *; do [-f "$ f"] && mv "$ f" foo; done`. Пожалуйста, обновите ваш вопрос результатами повторения обоих тестов, используя `ls -F` каждый раз и` mv -v` как в `find ... -exec`, так и в отдельной команде. Я использую Ubuntu 16.04.04 с `GNU bash, версия 4.3.48 (1) -релиз (x86_64-pc-linux-gnu)`. Если `foo` - это каталог, а` xyfooz.tex` - файл, мой автономный `mv` выдает` mv: не может переместить 'foo' в сам подкаталог, 'foo / foo'`, как и ожидалось. Какую ОС вы используете? AFH 6 лет назад 0
Я внес изменения в соответствии с вашим предложением, но они не говорят нам ничего нового. Я использую ту же версию Bash, которая уже была в вопросе. Я бы надеялся, что существует способ регулярного выражения, ограничивающий тип файла. Erwann 6 лет назад 0

1 ответ на вопрос

1
Kamil Maciorowski

С mv

cannot move 'foo' to a subdirectory of itselfбезвреден в этом случае, вы можете игнорировать это. Я имею в виду mvпереместить остальных, несмотря на предупреждение. Однако статус выхода не будет 0, это может быть серьезным препятствием в сценариях, где вы хотите прервать или предпринять корректирующее действие, если mvэто не удалось.

Об этом уже говорилось в комментарии : вы можете замолчать mv, перенаправив stderr с помощью 2>/dev/null; другие предупреждения или ошибки также будут перенаправлены.

Я думаю, что вы не можете легко «уточнить шаблон, чтобы ограничить исходный файл файлами, а не каталогами». Тем не менее, вы можете использовать подход, который не соответствует буквальному foo. Следующий подход не имеет ничего общего с файлами или каталогами; только с именами, потому что глобусы имеют дело с именами; так что в некоторых случаях этого может быть недостаточно.

*foo*Глоб соответствует четыре дизъюнктивных видов объектов:

  1. foo сам
  2. foo только с префиксом, как bar-foo- вы можете сопоставить их с помощью*?foo
  3. Foo с префиксом и постфикса, как bar-foo-baz-*?foo?*
  4. Фу только с постфиксом, как 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команда (с 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/уже открыты, не должны ни мешать, ни ломаться, потому что важен именно индекс. Однако, если какая-либо программа должна открыть файл (по пути в том числе 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не будет запускаться вообще;
  • благодаря --файлу подобное --fooне будет интерпретироваться как (неизвестная) опция mv;
Всеобъемлющее, просто хотел сказать спасибо, посмотрю на это. Erwann 6 лет назад 0