命令行查找,sed,exec

时间:2011-04-07 21:34:06

标签: bash scripting find exec

我在一个文件夹中,在子文件夹中有一堆文件,我试图制作某种单行程,以便偶尔快速复制/粘贴。

内容(此处粘贴时间太长):http://pastebin.com/4aZCPbwT

我尝试过以下命令:

列出所有文件及其目录

find . -name '[!.]*'

将“Namespace”的所有实例替换为“Test:

find . -name '[!.]*' -print0 | sed 's/Namespace/Test/gI' | xargs -i -0 echo '{}'

我需要做的是:

替换上面的foldes名称,并将文件夹(包括文件)复制到另一个位置。创建文件夹(如果它们不存在)(它们很可能不会) - 但是,有一些我不需要的文件夹,例如./app,因为此文件夹存在。我可以使用-wholename'。/ app'。

当它们被复制时,我需要替换每个文件中的一些文本,与上面相同(带有测试的命名空间 - 也会在文件中出现并保存它们当然)。

我想象的是这样的事情:

-print -exec sed -i 's/Namespace/Test/gI' {} \;

这三件事可以在单行中完成吗?替换文件中的文本(Namespace< => Test),使用cp -p复制包含其目录的文件(不想写入文件夹),但是如上所述重命名每个目录/文件(Namespace< =>检验)。

非常感谢: - )

2 个答案:

答案 0 :(得分:1)

除了在下面描述如何具有煞费苦心的详细程度之外,这种方法也可能是唯一的,因为它包含内置调试。它基本上没有做任何事情,除了编译和保存到变量它认为应该做的所有命令以执行所请求的工作。

它还尽可能明确地避免循环。除了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]*\(.*\)${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 sedfindfind printf来正确处理sed -z -e:;recursive regex test;tfork来电。如果您无法使用这些功能,则可能会通过一些小的调整来复制功能。

这应该从头到尾做你想做的一切,而且很少。我与sed进行了sed,但我也在练习一些rm -rf ${UNNECESSARY}递归分支技术,这就是我在这里的原因。我想,这有点像在理发学校打折。这是工作流程:

  • ./app
    • 我故意遗漏任何可能删除或破坏任何类型数据的函数调用。您提到\( -path PATTERN -exec rm -rf \{\} \)可能不受欢迎。删除它或事先将其移动到其他位置,或者,您可以构建find例程到_mvnfind "${@}"以编程方式执行此操作,但这一切都是您的。
  • ${sh_io}
    • 声明其参数并调用worker函数。 ${sed_sep}特别重要,因为它可以保存函数的返回值。 sed紧随其后;这是一个用于引用函数中${sed_sep}递归的任意字符串。如果将mv -n $1 $2设置为可能在您的任何路径或文件名中找到的值,那么......好吧,不要让它成为现实。
  • -noclobber
    • 整棵树从一开始就被移动了。它会省去很多头痛;相信我。您想要做的其余部分 - 重命名 - 只是文件系统元数据的问题。例如,如果您将其从一个驱动器移动到另一个驱动器,或跨越任何类型的文件系统边界,那么您最好一次使用一个命令执行此操作。它也更安全。请注意为mv设置的${SRC_DIR}选项;如上所述,此函数不会将${TGT_DIR}放在read -R SED <<HEREDOC已存在的位置。
  • find . -name ${OLD} -printf
    • 我在这里找到了所有sed的命令以节省逃避麻烦并将它们读入变量以提供给下面的sed。以下说明。
  • 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-depthmv的所有输出进行排序,以便最先处理与$ {SRC}关系最近的路径。这避免了将sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}文件放入不存在的位置时可能出现的错误,并且最大限度地减少了递归循环的需要。 (事实上,你可能很难找到一个循环
  • %Path
    • 我认为这是整个脚本中唯一的循环,它只会循环遍历为每个字符串打印的第二个sed,以防它包含多个可能需要替换的$ {OLD}值。我想象的所有其他解决方案都涉及第二个sed过程,虽然可能不需要短循环,但它确实会产生并分叉整个过程。
    • 所以基本上stdout在这里搜索$ {sed_sep},然后找到它,保存它和遇到的所有字符,直到找到$ {OLD},然后用$ {NEW替换它}。然后返回$ {sed_sep}并再次查找$ {OLD},以防它在字符串中出现多次。如果未找到,则将修改后的字符串打印到mv(然后再次捕获)并结束循环。
    • 这避免了必须解析整个字符串,并确保mv命令字符串的前半部分(当然需要包含$ {OLD})确实包含它,后半部分被更改为需要多次从sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out的目标路径中删除$ {OLD}名称。
  • -exec
    • 这里发出的两个fork来电没有第二个mv。首先,正如我们所见,我们根据需要修改find -printf函数命令提供的sed命令,以正确更改$的所有引用{OLD}到$ {NEW},但为了做到这一点,我们不得不使用一些不应包含在最终输出中的任意参考点。因此,一旦read完成它需要做的所有事情,我们就会指示它在传递它之前从保持缓冲区中清除它的参考点。

现在我们重新回来

% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000 将收到如下命令:

read

${msg}将其${sh_io}改为{{1}}作为{{1}},可以在函数外部随意查看。

冷却。

-Mike

答案 1 :(得分:0)

我没有测试过这个,但我认为这就是你所追求的。

find . -name '[!.]*' -print | while read line; do nfile=`echo "$line" | sed 's/Namespace/Test/gI'`; mkdir -p "`dirname $nfile`"; cp -p "$line" "$nfile"; sed -i 's/Namespace/Test/gI' "$nfile"; done