我必须在 Eclipse 插件项目中进行一些全局重命名,尽管代码是 Java 并且它是 Eclipse 插件这一事实是无关紧要的。
基本上,我需要执行的步骤如下:
find . -name "*" -type f | grep -v \.git | xargs sed -i 's/com.foo/org.bar/g
- 对于 中的所有目录(不包括
.git
)src/com
,重命名为src/org
- 对于 中的所有目录(不包括
.git
)src/org/foo
,重命名为src/org/bar
com.foo
对于名称中包含的所有文件和目录名称(不包括 .git) ,将该部分更改为org.bar
.
我只有一个明确的命令行用于第一步。
我可以设法半手动完成其他步骤,但我认为确定一种自动化方法来执行此操作是值得的。第二步和第三步是等效的,但第四步不同。
我想用纯 bash 命令行来完成此操作,但我可以满足于简单的 bash 或 perl 脚本。
更新:
事实上,我需要寻找的不仅仅是src/com
。我还需要src/main/java/com
和src/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
并且-path
infind 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
包含子字符串的所有路径,它不适用于包含空格或 的路径,它用和\'"
之间的任何字符替换输入。最后一个问题很容易通过在点前添加反斜杠来解决。文件名的问题可能是也可能不是您的设置中的问题 - 源树往往很温和。也是多余的(它总是匹配的)。这是一个安全且更简单的替代方案,可以正确省略子树。com
foo
-name "*"
.git
find . -name .git -prune -o -type f -exec sed -i 's/com\.foo/org.bar/g' {} +
这仍然用 代替 例如com.fooalso
,org.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'