У меня нет смелости сделать это снова, но я написал это в ответ на Командная строка Find Sed Exec .Там спрашивающий хотел знать, как переместить все дерево, возможно, исключая один или два каталога, и переименовать все файлы и каталоги, содержащие строку "OLD" , чтобы вместо нее содержать "NEW" .
Помимо , описывающего how с кропотливым многословием ниже, этот метод также может быть уникальным в том, что он включает встроенную отладку.По сути, он вообще ничего не делает, как написано, за исключением компиляции и сохранения в переменную всех команд, которые он считает необходимыми для выполнения запрошенной работы.
Он также явно избегает циклов как можно больше.Кроме рекурсивного поиска 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
и 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
-printf
, по мере необходимости, чтобы должным образом изменить все ссылки $ {OLD} на $ {NEW}, но вДля этого нам пришлось использовать несколько произвольных опорных точек, которые не должны быть включены в окончательный результат.Поэтому, как только sed
завершит все, что ему нужно, мы даем указание стереть свои контрольные точки из буфера хранения, прежде чем передать его дальше.
И СЕЙЧАС НАЗАД В ТЕЧЕНИЕ
read
получит команду, которая выглядит следующим образомэто:
% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000
Это read
превратится в ${msg}
в ${sh_io}
, что можно проверить по желанию вне функции.
Cool.
-Майк