Как переместить неизвестные файлы в неизвестные каталоги по имени папки?

Я пытаюсь написать скрипт, который будет искать каталог для папки и перемещать содержимое папок на один каталог вверх от их местоположения. это то, что я имею до сих пор:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

Я думаю, что моя проблема заключается в каталоге назначения ... возможно я выражаю это неправильно?

Любая помощь полезна.

* редактировать **

, чтобы упростить эту проблему. Более подробная информация

Я из организации, из которой она пытается уйти MS Exchange для Zimbra. В рамках нашей миграции мы нашли отличные утилиты, которые переносят почтовые ящики, но передача файлов PST в пригодный для использования формат Zimbra оказывается сложной задачей.

Я нашел несколько решений, хотя для краткости я смог раскрутить pst-файлы в пригодный для использования формат с одним исключением -

вместо того, чтобы помещать почту в эту папку. создается подкаталог с именем “mbox”, по-видимому, напоминающий файл mbox, из которого вырываются файлы .eml. Поскольку каждый пользователь отличается и использует разные структуры папок от других пользователей, каждый каталог неизвестен до тех пор, пока не будет запущен сценарий. Кроме того, содержимое папки mbox также является уникальным.

Поскольку папка с именем mbox является единственной константой в этих каталогах, я пытаюсь использовать команду find, чтобы найти эти папки, взять содержимое этих папок и переместить их в один каталог, а затем удалить папку с именем mbox. Вот фактическая команда, как она появляется в моем сценарии:

find localFolders -name mbox -type d -exec mv {}/* .. \;

или в приведенном вами примере:

find localFolders -name mbox -type d -execdir sh -c 'echo mv {}/* ..' \;

Я пытаюсь использовать * в качестве подстановочного знака выбрать все в папке mbox. Насколько я могу судить, вместо этого он рассматривается как часть каталога, а не как подстановочный знак, как предполагалось

мысли?

2
задан 29 June 2013 в 11:29

2 ответа

Как насчет этого?

find ./localFolders -name 'file' -type d -execdir bash -c 'mv -vnt . -- "$0"/*' {} \; -prune

Безопасный относительно пробелов и забавных символов в именах файлов.Приятного отдыха!

1
ответ дан 29 June 2013 в 11:29

Чтобы правильно делать то, что вы хотите, используя find, вы должны понимать следующие понятия:

  1. Рабочий каталог процесса.
  2. Глобальная особенность bash (или других оболочек).

Каждый процесс в linux имеет рабочий каталог как часть своего внутреннего состояния. Вы можете представить его как «местоположение» процесса в файловой системе. Оболочка также является процессом и также имеет рабочий каталог. Рабочий каталог оболочки - это каталог (обычно), отображаемый в вашем приглашении.

Когда процесс создается, он наследует рабочий каталог от своего родителя. Если вы запускаете процесс из оболочки, то оболочка является родительской. Каждый процесс может изменить свой рабочий каталог по желанию. Вы можете изменить рабочий каталог оболочки, используя встроенную команду cd.

Рабочий каталог используется при работе с относительными именами файлов. Относительные имена файлов - это имена файлов, которые не начинаются с /. Например, когда вы делаете cat foo, он ищет файл foo в текущем рабочем каталоге, но когда вы делаете cat /tmp/foo, он ищет файл foo в /tmp.

При работе с find -exec вам всегда нужно думать о рабочем каталоге. Рабочий каталог -exec является рабочим каталогом find. Рабочий каталог find является рабочим каталогом оболочки.

В вашем случае:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

.. будет ссылаться на родительский каталог рабочего каталога mv, который является рабочим каталогом -exec, который является рабочим каталогом find, который является рабочим каталогом оболочки к моменту выполнения команды. Это не то, что вы хотели.

Существует -execdir, который похож на -exec, но сначала он переключает рабочий каталог в каталог, где найден соответствующий элемент. -execdir - это огромный шаг к решению вашего вопроса, но в вашей команде find есть еще одна проблема: * a.k.a. glob.

Глоб - это что-то вроде подстановочного знака. Например, foo* будет соответствовать foobar и foobazfoo, потому что * также соответствует пустой строке). Все идет нормально. Интересная часть globbing заключается в том, что это выполняется оболочкой, а не исполняемой командой. Когда вы выполняете rm foo*, оболочка сначала расширяет * до foobar и foobaz, а затем выполняет rm foobar foobaz. rm никогда не видит *.

Что происходит, если в текущем каталоге нет файлов, начинающихся с foo? Тогда оболочка (обычно) не расширит * и передаст * команде дословно. Это означает, что rm foo* будет выполняться как rm foo*. rm будет искать файл с именем foo*, и он (скорее всего) не найдет его и прервет с ошибкой. Если вам интересно: да, имя файла может содержать *, нет, не пытайтесь сделать это дома.

В вашем случае:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

Перед выполнением команды find оболочка сначала попытается расширить глобус {}/*. Нет файлов, соответствующих шаблону {}/*, поэтому глобус будет передан дословно.

Посмотрим, что делает команда find. Предположим, вы выполняете команду find из /home/username/somedir, и существует каталог /home/username/somedir/localFolders/foodir/file с некоторыми файлами в нем. find начнет поиск в /home/username/somedir/localFolders, потому что это указано в параметрах, но рабочий каталог find - /home/username/somedir, потому что он был выполнен оттуда.

Теперь find найден каталог /home/username/somedir/localFolders/foodir/file. Перед выполнением -exec он заменит {} найденным элементом. Это означает, что mv {}/* .. станет mv /home/username/somedir/localFolders/foodir/file/* ... mv потерпит неудачу, потому что он (скорее всего) не найдет файл с именем *.

Предположим, что существует файл с именем *, затем mv переместит файл в ... Помните, что рабочим каталогом является /home/username/somedir. Это означает, что mv переместит файл в /home/username/somedir/.., то есть /home/username. Это не то, что вы хотели.

«Исправление» для вашей команды find зависит от нескольких факторов. Например:

  • Есть ли «точечные файлы» в каталоге file? Dotfiles - это файлы с именами, начинающимися с точки. Глобус * (обычно) не будет расширяться до имен файлов, начинающихся с точки.
  • Есть ли другие каталоги в каталоге file? find попытаются вернуться к ним, но потерпят неудачу, потому что они были перемещены.
  • Есть ли каталоги с именем file в каталоге file? Куда должны идти эти файлы?
  • Есть ли в каталоге file файлы gazillion? Это может испортить расширение глобуса *.
  • Есть ли конфликты имен файлов с файлами из каталога file? Должны ли они быть перезаписаны?
  • Есть ли пробелы в именах файлов? Это все испортит.

Чтобы исправить вашу команду find, я с радостью проигнорирую все эти факторы.

Вот исправление, использующее -execdir и sh -c для задержки расширения *:

find ./localFolders -name file -type d -execdir sh -c 'mv {}/* ..' \;

Поскольку * находится в кавычках, оно не будет расширено раньше времени. Когда -execdir выполнит команду, sh -c развернет * в рабочем каталоге.

Вот еще одно исправление без sh -c:

find ./localFolders -path "*/file/*" -execdir mv {} .. \;

Я использовал -path, чтобы найти все элементы в каталогах с именем file. * заключены в кавычки, поэтому они не будут расширены раньше времени. На этот раз find занимается интерпретацией *. Обратите внимание, что это также будет соответствовать что-то вроде ./localFolders/foodir/file/bardir/file/somefile. Как я уже сказал, я с радостью проигнорировал этот фактор.

И ради полноты еще одно исправление без -execdir:

find ./localFolders -name file -type d -exec sh -c 'mv {}/* {}/..' \;
0
ответ дан 29 June 2013 в 11:29

Другие вопросы по тегам:

Похожие вопросы: