1

1

我必须在 Eclipse 插件项目中进行一些全局重命名,尽管代码是 Java 并且它是 Eclipse 插件这一事实是无关紧要的。

基本上,我需要执行的步骤如下:

  1. find . -name "*" -type f | grep -v \.git | xargs sed -i 's/com.foo/org.bar/g
  2. 对于 中的所有目录(不包括.gitsrc/com,重命名为src/org
  3. 对于 中的所有目录(不包括.gitsrc/org/foo,重命名为src/org/bar
  4. com.foo对于名称中包含的所有文件和目录名称(不包括 .git) ,将该部分更改为org.bar.

我只有一个明确的命令行用于第一步。

我可以设法半手动完成其他步骤,但我认为确定一种自动化方法来执行此操作是值得的。第二步和第三步是等效的,但第四步不同。

我想用纯 bash 命令行来完成此操作,但我可以满足于简单的 bash 或 perl 脚本。

更新:

事实上,我需要寻找的不仅仅是src/com。我还需要src/main/java/comsrc/test/java/com

为此,我一直在尝试这样的事情:

find . -type d -name "com" | grep "\(/src/com\|/src/main/java/com\|/src/test/java/com\)" | xargs -n1 -i{} echo mv {} $(echo {} | sed -e 's/com/org/g')

这很接近,但最后一个sed不匹配,所以它不会改变它。其结果如下:

mv ./plugins/.../src/com ./plugins/.../src/com

答案1

1

对于第一步,在命令示例中,您不需要,*因为它不会过滤任何文件。您需要做的是过滤.git,如下所示:

$ find . -name '.git' -prune -o -type f -print

这将拒绝 ( -prune) 任何完全命名的目录.git及其中的所有内容。

这就消除了grep -v ".git". sed 是不可避免的,因为它正在编辑文件的内部,而不是 find 可以做的事情。

但我们仍然可以简化(引用 sed 中的点):

$ find . -name '.git' -prune -o -type f -exec sed -i 's/com\.foo/org\.bar/g' '{}' \+

这会将文件名(类似于 xargs)累积到sed命令中。

但更好的是:

$ find . -name '.git' -prune -o -type f -execdir sed -i 's/com\.foo/org\.bar/g' '{}' \+

这将为每个目录执行一个 sed 实例。

2、和3

让我们定义一个函数来执行目录重命名:

$ renamedir (){ find "$1" -name '.git' -prune -o -type d -exec bash -c 'mv "$1" "${1//"$2"/$3}"' sh '{}' "$2" "$3" \; ; }

第2步:

  • 将 src/com 中的所有目录(不包括 .git)重命名为 src/org

    $ renamedir 'd/src/com' 'src/com' 'src/org'
    

步骤3:

  • 对于 src/org/foo 中的所有目录(不包括 .git)重命名为 src/org/bar

    $ renamedir 'src/org/foo' 'ogr/foo' 'org/bar'
    

4

步骤4:

  • 对于名称中包含“com.foo”的所有文件和目录名称(不包括 .git),将该部分更改为“org.bar”。

    $ find . -name '.git' -prune -o -name "com.foo" -type f -exec bash -c 'mv "$1" "${1//"$2"/$3}"' sh '{}' "com.foo" "org.bar" \; ; }
    

全部位于(格式更好的)脚本内:

#!/bin/bash

# NOTE: There are three echo to avoid execution of commands.
#       If after testing, you decide that it is ok to execute commands,
#       remove the echo words.

# Step 1:
### for all files (excluding .git) in . change
###     file contents from com.foo to org.bar
find . -name '.git' -prune -o -type f -execdir echo sed -i 's/com\.foo/org\.bar/g' '{}' \+

renamedir (){ find "$1" -name '.git' -prune -o -type d -exec bash -c '
                  echo \
                  mv "$1" "${1//"$2"/$3}"
              ' sh '{}' "$2" "$3" \;
        }

# Step 2:
### for all directories (excluding .git) in src/com rename to src/org
renamedir 'd/src/com' 'src/com' 'src/org'

# Step 3:
### for all directories (excluding .git) in src/org/foo rename to src/org/bar
renamedir 'src/org/foo' 'org/foo' 'org/bar'

# Step 4:
### for all file and directory names (excluding .git)
### with "com.foo" in the name, change that portion to "org.bar".
find . -name '.git' -prune -o -name "*com.foo*" -type f -exec bash -c '
    echo \
    mv "$1" "${1//"$2"/$3}"
' sh '{}' "com.foo" "org.bar" \; ; }

要测试不同版本的匹配,请创建一个完整的文件树,如下所示(稍后使用 删除它rm -rf ./d/):

$ mkdir -p d/src/{{,.git}{,one},com/{{,.git}{,two},\ .git,.git/six},org/{{,.git}{,three},bar/{,.git}{,four},foo/{,.git}{,five}}}
$ touch d/src/{111,com/{222,.git/666},org/{333,bar/444,foo/555}}  
$ touch d/src/{.git/{aaa,one/aaaaa},one/aaa111,com/.git{/bbb,two/bbb222},org/{.git{/ccc,three/ccc333},bar/.git{/ddd,four/ddd444},foo/.git{/eee,five/eee555}}}               

您的原始命令(使用 GNU -z 选项来匹配 find -print0)将匹配(在此目录中):

$ find d/ -type f -print0 | grep -zZv \.git
find d/ -type f -print0 | grep -zZv \.git | xargs -0 printf '<%s>\n'
<d/src/org/333>
<d/src/org/foo/555>
<d/src/org/bar/444>
<d/src/com/222>
<d/src/111>

只有五个文件,与任何事情都没有关系.git


一般来说:

.git中的匹配主要有以下三种方式find-name-regex-path。除了-regex奇怪之外(使用 emacs 正则表达式),只需要复杂的匹配,而这里不需要。

Afind d/ -path "./.git"将完全匹配:只有一个中间.git目录。
或者(对于创建的目录)find . -path './d/src/.git'将仅匹配./d/src/.git

A将在树的最后一层(基本名称)find d/ -name ".git"完全匹配。.git

两者-name "*.git"-path "*.git"都会匹配得完全相同。
任何字符串结束.git

当我们添加两个星号时,事情开始变得复杂:*.git*
在这种情况下,-name只会匹配路径的最后一级(文件的基本名称),但由于两个星号*也会匹配带有前缀的名称(注意空格)在.git)和后缀(注意很多类似.gitone):

$ find d -name '*.git*'
d/src/org/.gitthree
d/src/org/foo/.git
d/src/org/foo/.gitfive
d/src/org/bar/.gitfour
d/src/org/bar/.git
d/src/org/.git
d/src/com/.gittwo
d/src/com/.git
d/src/com/ .git
d/src/.git
d/src/.gitone

.git在路径的任何级别进行匹配,几乎与 grep 一样。与 grep 不完全相同,因为 grep 可能具有复杂的正则表达式,但这里的匹配是针对简单的“模式”。请注意d/src/com/.git/six以下事项:

$ find d -path '*.git*'
d/src/org/.gitthree
d/src/org/foo/.git
d/src/org/foo/.gitfive
d/src/org/bar/.gitfour
d/src/org/bar/.git
d/src/org/.git
d/src/com/.gittwo
d/src/com/.git
d/src/com/.git/six
d/src/com/ .git
d/src/.git
d/src/.gitone

然后我们可以通过多种方式修改此类匹配,使用 (not!) 会拒绝以下匹配的内容:

不是 (!)

如果find d -name '.git'匹配:

d/src/org/foo/.git
d/src/org/bar/.git
d/src/org/.git
d/src/com/.git
d/src/.git

那么find d -not -name '.git'(或者,完全等价find d ! -name '.git':)将匹配所有其他的。由于列表很长,此处不打印。

但仅选择文件很短(注意 file 666):

$ find d -not -name '.git' -type f
d/src/org/333
d/src/org/foo/555
d/src/org/bar/444
d/src/com/222
d/src/com/.git/666
d/src/111

并且-pathinfind d -not -path '*.git*' -type f将仅匹配五个文件:

$ find d -not -path '*.git*' -type f
d/src/org/333
d/src/org/foo/555
d/src/org/bar/444
d/src/com/222
d/src/111

修剪

或者我们可以使用-prune“剪切”或“删除”所有目录以前的接受的比赛:

$ find d \( -name '.git' -prune \) -o \( -print \)
d
d/src
d/src/org
d/src/org/three
d/src/org/333
d/src/org/.gitthree
d/src/org/foo
d/src/org/foo/five
d/src/org/foo/555
d/src/org/foo/.gitfive
d/src/org/bar
d/src/org/bar/.gitfour
d/src/org/bar/four
d/src/org/bar/444
d/src/one
d/src/com
d/src/com/.gittwo
d/src/com/222
d/src/com/two
d/src/com/ .git
d/src/111
d/src/.gitone

-prune 之后一定有东西。通常是“-o”,这里还有一个-print来匹配 prune 不匹配的所有内容。正如你所看到的,这个匹配比你的命令的五个文件要多得多。更改'.git''*.git'只会删除d/src/com/ .git.但使用'*.git*'将更接近 grep (仍然不完全相同):

$ find d \( -name '*.git*' -prune \) -o \( -print \)
d
d/src
d/src/org
d/src/org/three
d/src/org/333
d/src/org/foo
d/src/org/foo/five
d/src/org/foo/555
d/src/org/bar
d/src/org/bar/four
d/src/org/bar/444
d/src/one
d/src/com
d/src/com/222
d/src/com/two
d/src/111

这将仅匹配命令的五个文件(不是目录):

$ find d \( -name '*.git*' -prune \) -o \( -type f -print \)

这也将仅匹配最初的五个文件。添加-type f隐藏了很多细节:

$  find d \( -name '.git' -prune \) -o \( -type f -print \)

通常不带括号(不太清楚):

$ find d -name '.git' -prune -o -type f -print

警告:删除-print可能会产生意想不到的副作用。

答案2

在文件中替换

你的命令由于多种原因不可靠:它排除.git包含子字符串的所有路径,它不适用于包含空格或 的路径,它用和\'"之间的任何字符替换输入。最后一个问题很容易通过在点前添加反斜杠来解决。文件名的问题可能是也可能不是您的设置中的问题 - 源树往往很温和。也是多余的(它总是匹配的)。这是一个安全且更简单的替代方案,可以正确省略子树。comfoo-name "*".git

find . -name .git -prune -o -type f -exec sed -i 's/com\.foo/org.bar/g' {} +

这仍然用 代替 例如com.fooalsoorg.baralso如果发生这种情况可能是不可取的。这是一个更安全的结构:

find . -name .git -prune -o -type f -exec sed -i -e 's/com\.foo$/org.bar/' -e 's/com\.foo\([^-_0-9A-Za-z]\)/org.bar\1/' {} +

使用 zsh 重命名文件

加载zmv函数autoload -U zmv,然后你可以用它来重命名文件通配符模式。我认为你正在寻找的实际上是

zmv -w '**/com/foo' '$1/org/bar'
zmv -w '**/*com.foo' '$1/${2}org.bar'
zmv '(**/)(*)com.foo([^-_0-9A-Za-z]*)' '$1${2}org.bar$3'

相关内容