我没有全心全意再做一次,但是我写这篇文章是为了回答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
andsed -z -e
和:;recursive regex test;t
call。如果您无法使用这些功能,则可以通过少量调整来复制该功能。
从头到尾,这应该可以做所有您想要的事情,而不必大惊小怪。我曾经fork
使用过sed
,但是我也在练习一些sed
递归分支技术,所以这就是为什么我在这里。我想这有点像在理发学校打折理发。这是工作流程:
rm -rf ${UNNECESSARY}
- 我故意忽略了可能删除或破坏任何类型数据的任何函数调用。您提到那
./app
可能是不必要的。事先删除它或将其移动到其他位置,或者,您可以构建一个\( -path PATTERN -exec rm -rf \{\} \)
例程来find
以编程方式进行操作,但这就是您的全部。
_mvnfind "${@}"
- 声明其参数并调用worker函数。
${sh_io}
之所以特别重要,是因为它节省了函数的返回值。${sed_sep}
紧随其后 这是用于引用sed
函数中的递归的任意字符串。如果${sed_sep}
将if设置为可能在您作用的任何路径名或文件名中都可能找到的值……那么,那就别让它如此。
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}
可以在函数的意志之外进行检查。
凉。
-麦克风