У меня нет духа делать это снова и снова, но я написал это в ответ на Commandline Find Sed Exec . Здесь спрашивающий хотел знать, как переместить все дерево, возможно, исключая один или два каталога, и переименовать все файлы и каталоги, содержащие строку «OLD», чтобы вместо этого содержать «NEW» .
Помимо описания того, как с кропотливой детализацией ниже, этот метод также может быть уникальным тем, что включает встроенную отладку. По сути, он вообще ничего не делает, как написано, за исключением компиляции и сохранения в переменной всех команд, которые, по его мнению, должны выполняться для выполнения запрошенной работы.
Он также явно избегает циклов, насколько это возможно. Насколько мне известно, кроме sed
рекурсивного поиска более чем одного совпадения с шаблоном, другой рекурсии не существует.
И, наконец, он полностью null
разделен - он не срабатывает ни с одним символом в любом имени файла, кроме null
. Я не думаю, что тебе это нужно.
Кстати, это ДЕЙСТВИТЕЛЬНО быстро. Смотреть:
% _mvnfind() { mv -n "${1}" "${2}" && cd "${2}"
> read -r SED <<SED
> :;s|${3}\(.*/[^/]*${5}\)|${4}\1|;t;:;s|\(${5}.*\)${3}|\1${4}|;t;s|^[0-9]*[\t]\(mv.*\)${5}|\1|p
> SED
> find . -name "*${3}*" -printf "%d\tmv %P ${5} %P\000" |
> sort -zg | sed -nz ${SED} | read -r ${6}
> echo <<EOF
> Prepared commands saved in variable: ${6}
> To view do: printf ${6} | tr "\000" "\n"
> To run do: sh <<EORUN
> $(printf ${6} | tr "\000" "\n")
> EORUN
> EOF
> }
% rm -rf "${UNNECESSARY:=/any/dirs/you/dont/want/moved}"
% time ( _mvnfind ${SRC=./test_tree} ${TGT=./mv_tree} \
> ${OLD=google} ${NEW=replacement_word} ${sed_sep=SsEeDd} \
> ${sh_io:=sh_io} ; printf %b\\000 "${sh_io}" | tr "\000" "\n" \
> | wc - ; echo ${sh_io} | tr "\000" "\n" | tail -n 2 )
<actual process time used:>
0.06s user 0.03s system 106% cpu 0.090 total
<output from wc:>
Lines Words Bytes
115 362 20691 -
<output from tail:>
mv .config/replacement_word-chrome-beta/Default/.../googlestars \
.config/replacement_word-chrome-beta/Default/.../replacement_wordstars
ПРИМЕЧАНИЕ. Для приведенного выше function
, вероятно, потребуются GNU
версии sed
и find
для правильной обработки вызовов find printf
and sed -z -e
и :;recursive regex test;t
. Если они вам недоступны, функциональность, вероятно, может быть продублирована с небольшими изменениями.
Это должно сделать все, что вы хотели, от начала до конца без особых усилий. Я сделал fork
с sed
, но я также практикуя некоторые sed
рекурсивные методы ветвления так вот почему я здесь. Полагаю, это похоже на стрижку со скидкой в парикмахерской. Вот рабочий процесс:
rm -rf ${UNNECESSARY}
- Я намеренно пропустил любой функциональный вызов, который мог бы удалить или уничтожить данные любого рода. Вы упомянули, что это
./app
может быть нежелательно. Удалите его или переместите в другое место заранее, или, в качестве альтернативы, вы можете создать \( -path PATTERN -exec rm -rf \{\} \)
процедуру, find
чтобы делать это программно, но это все ваше.
_mvnfind "${@}"
- Объявите его аргументы и вызовите рабочую функцию.
${sh_io}
особенно важен тем, что сохраняет возврат из функции. ${sed_sep}
занимает второе место; это произвольная строка, используемая для ссылки sed
на рекурсию в функции. Если ${sed_sep}
установлено значение, которое потенциально может быть найдено в любом из ваших путей или имен файлов, на которые воздействовали ... ну, просто не позволяйте этому быть.
mv -n $1 $2
- Все дерево перемещается с самого начала. Это избавит от головной боли; поверь мне. Остальное, что вы хотите сделать - переименование - просто вопрос метаданных файловой системы. Если вы, например, перемещали это с одного диска на другой или через границы файловой системы любого типа, вам лучше сделать это сразу с помощью одной команды. Это также безопаснее. Обратите внимание на
-noclobber
параметр, установленный для mv
; как написано, эта функция не будет помещена ${SRC_DIR}
там, где ${TGT_DIR}
уже существует.
read -R SED <<HEREDOC
- Я разместил здесь все команды sed, чтобы избежать неприятностей, и прочитал их в переменной для передачи в sed ниже. Пояснение ниже.
find . -name ${OLD} -printf
- Начинаем
find
процесс. С помощью find
мы ищем только то, что нужно переименовать, потому что мы уже выполнили все mv
операции размещения с первой командой функции. Вместо того, чтобы предпринимать какие-либо прямые действия find
, такие как exec
, например, вызов, мы используем его для динамического построения командной строки с помощью -printf
.
%dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
- После
find
того, как мы найдем нужные нам файлы, он будет напрямую строить и распечатывать ( большую часть) команды, которая нам понадобится для обработки вашего переименования. %dir-depth
Пришитые начало каждой строки будет способствовать тому , чтобы мы не пытались переименовать файл или папку в дереве с родительским объектом , который еще должен быть переименован. find
использует всевозможные методы оптимизации для обхода дерева вашей файловой системы, и нет уверенности, что оно вернет нужные нам данные в безопасном для операций порядке. Вот почему мы следующие ...
sort -general-numerical -zero-delimited
- Мы сортируем весь
find
вывод на основе, %directory-depth
так что пути, ближайшие по отношению к $ {SRC}, обрабатываются первыми. Это позволяет избежать возможных ошибок, связанных с mv
загрузкой файлов в несуществующие места, и сводит к минимуму необходимость в рекурсивном цикле. ( на самом деле, вам может быть трудно найти цикл вообще )
sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
- Я думаю, что это единственный цикл во всем скрипте, и он перебирает только второй,
%Path
напечатанный для каждой строки, если он содержит более одного значения $ {OLD}, которое может нуждаться в замене. Все другие решения, которые я придумал, включают второй sed
процесс, и хотя короткий цикл может быть нежелательным, безусловно, лучше, чем создание и разветвление всего процесса.
- Итак, в основном то,
sed
что здесь делается, - это поиск $ {sed_sep}, затем, найдя его, сохраняет его и все встречающиеся символы, пока не найдет $ {OLD}, который затем заменяет на $ {NEW}. Затем он возвращается к $ {sed_sep} и снова ищет $ {OLD}, если он встречается в строке более одного раза. Если он не найден, он печатает измененную строку stdout
(которую затем снова захватывает) и завершает цикл.
- Это позволяет избежать синтаксического анализа всей строки и гарантирует, что первая половина
mv
командной строки, которая, конечно, должна включать $ {OLD}, действительно включает ее, а вторая половина изменяется столько раз, сколько необходимо, чтобы стереть Имя $ {OLD} из mv
пути назначения.
sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
- Два
-exec
звонка здесь происходят без секунды fork
. В первой, как мы уже видели, мы изменяем mv
команду, поставляемый find
«s -printf
команды функции , как необходимо правильно изменить все ссылки $ {OLD} до $ {NEW}, но для того , чтобы сделать это , мы должны были использовать некоторые произвольные ориентиры, которые не следует включать в окончательный результат. Итак, как только sed
закончится все, что ему нужно сделать, мы даем ему указание стереть свои контрольные точки из буфера удержания перед тем, как передать его.
И теперь мы вернулись
read
получит команду, которая выглядит так:
% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000
Это будет read
его в ${msg}
качестве ${sh_io}
которой может быть рассмотрена по желанию за пределы функции.
Круто.
-Майк