我正在尝试重命名我的应用程序中的许多文件,并且需要能够在应用程序根目录下通过git(即git mv%filenamematch %% replacement%)在所有子目录中进行重命名,只替换匹配的文本。 我对bash脚本没有好处。
更新:如果还重命名匹配的目录也会很好!
-
你有一个例子吗? 什么'%filenamematch%'喜欢; 它总是在开头或结尾或中间或什么? %replacement%是什么样的?
-
它可能是person.rb或lookitisa_person_here.html。 在这两种情况下,人都需要匹配并从那里改变为其他东西。 它也应该区分大小写
这应该做的伎俩:
1
| for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); git mv $file $(echo $file | sed -e 's/%filenamematch%/%replacement%/') |
要了解这是做什么的,你需要用"|"来理解管道并用"$(...)"替换命令。这些强大的shell结构允许我们组合几个命令来获得我们需要的结果。请参阅管道和命令替换。
以下是这个单行中发生的事情:
git ls-files:这会在Git存储库中生成一个文件列表。它类似于你从ls得到的,除了它只输出Git项目文件。从此列表开始确保您的.git /目录中没有任何内容被触及。
| grep %filenamematch%:我们从git ls-files获取列表并通过grep将其过滤以将其过滤为仅包含我们正在寻找的单词或模式的文件名。
| sed -e 's/\(%filenamematch%[^/]*\).*/\1/':我们通过sed(流编辑器)管理这些匹配,执行(-e)sed的s(替换)命令来切断匹配目录后的任何/后续字符(如果它恰好是一个)。
| uniq:如果匹配是一个目录,现在我们已经切断了包含的目录和文件,可能会有很多匹配的行。我们使用uniq将它们全部组合成一行。
for file in ...:shell的"for"命令将遍历列表中的所有项目(文件名)。依次为每个文件名,它分配给变量"$ file",然后在分号(;)之后执行命令。
sed -e 's/%filenamematch%/%replacement%/':我们使用echo通过sed管道每个文件名,再次使用它的替换命令 - 这次在文件名上执行模式替换。
git mv:我们使用这个git命令将现有文件($ file)转换为新文件名(由sed更改的文件名)。
更好地理解这一点的一种方法是单独观察这些步骤。为此,请在shell中运行以下命令,并观察输出。所有这些都是非破坏性的,只为您的观察产生列表:
git ls-files
git ls-files | grep %filenamematch%
git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/'
git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq
for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); echo $file
for file in $(git ls-files | grep %filenamematch% | sed -e 's/\(%filenamematch%[^/]*\).*/\1/' | uniq); echo $file | sed -e 's/%filenamematch%/%replacement%/'
-
你有没有机会解释这是做什么的?我不明白。另外,我更新了我对我的问题的评论,文件名匹配可能是要更改的文件名的一部分,因为它可能是some_person.html或person.rb,在这两种情况下,只有'person'部分应该匹配和更改
-
当然。这是那些看起来有点令人生畏的bash one-liners之一。也许其他人会建议使其更简洁或可读。我会添加一些解释性说明。
-
注意:此命令确实存在缺陷:如果您有任何与替换模式匹配的目录,则会导致错误。换句话说,它将尝试使用git mv path/person/file.rb path/alien/file.rb进行移动,如果目标目录不存在,则可能会失败。为了解决这个问题,我们可以为我们正在搜索的模式添加一些逻辑,以确保匹配的字符串后面没有斜杠。
-
我忽略了另外一件事,它应该适用于目录!有可能包含这个吗?这将是非常有效的!更新:是的我注意到,在这种情况下 - 目录匹配也重命名实际上也是有益的
-
更强大的模式看起来像这样:for file in $(git ls-files | grep '%filenamematch%[^/]*$); git mv $file $(echo $file | sed -e 's/%filenamematch%/%replacement%[^/]*$/')但是,我需要测试以确保我正确使用grep和sed的正则表达式语法。它们并不总是和我以前一样。
-
喔好吧。唯一的问题是,如果目标目录不存在,目录重命名可能会出错。我不确定git mv是如何处理的。
-
关于Git的好处是你可以尝试这个,并且如果它造成任何伤害总是会恢复。
-
如果目录不存在,我认为需要先创建它。 git mv -f不会。
-
stackoverflow Jonathan的精彩介绍回答,这节省了大量应用程序IMO命名错误的时间。我认为这个脚本非常有用。如果它也可以重命名,那就更好了
-
啊,这真的是一个剧本。 :)我们必须写一个小Git瓷器命令。嗯。
-
好的,我添加了一些更多的sed魔法来处理目录和文件的移动。 git mv [directory]适用于我的测试。我们只需要将其删除到目录即可实现。
-
剩下的周数是你有路径/匹配/.../ match / ....只有第一场比赛才会被移动。之后,您可以再次运行该命令,直到没有留下任何落后者 - 只要您的新文件名不包含旧文件名。因为当然,任何事都不容易。 :)
-
在那,事实证明你可能根本不需要sed:do git mv $file ${file//old/new}。见:mywiki.wooledge.org/BashFAQ/100
-
那很酷。我猜这只是Bash,对吧?不是说我的答案是在shell中测试的,但我认为大多数应该在sh中工作。谢谢你的提示。
-
我不得不用do %git mv ....%; done包围git move语句,使其工作如cyberciti.biz/faq/linux-unix-bash-for-loop-one-line-command所述
-
bash: syntax error near unexpected token 'git'
-
@feos等:看看Gregg Lind的答案,它有一个bash选项
使用命令rename使用正则表达式重命名文件:
然后通过添加新文件并删除旧文件,使用Git注册更改:
1 2
| git add .
git ls-files -z --deleted | xargs -0 git rm |
在较新版本的Git中,您可以使用--all标志:
-
这与git mv一个文件相同吗?历史保存了吗?
-
是的,它是一样的。 git mv始终与git rm和git add相同,Git不会像其他版本控制系统那样存储其他元数据。
-
很好的方法,很容易理解。但对我来说,rename old new *是每个文件名(*)中第一次出现的new被替换为old。
-
TIL --all标志。我认为这是最明智的方法。
-
这正是我所寻找的。 rename已经提供了一种重命名多个文件的简洁方法。我正在寻找类似git rename而不是git mv的东西,这通过使用rename后跟一个简单的git add --all来实现相同的工作。这里是最好的解决方案。
迟到了,但是,这应该在BASH中工作(对于文件和目录,但我对目录要小心):
1
| find . -name '*foo*' -exec bash -c 'file={}; git mv $file ${file/foo/bar}' \; |
-
如果您解释在脚本之前和之后的位置,那将会很酷。在*foo*之间,/foo/bar foo =之前bar =之后是我的假设
-
是。以前是'foo'(你之前评论中的人)。之后是'酒吧'。 BASH shell语法$ {variable / pattern-to-match / replace-with}用于生成新名称。
-
我在Ubuntu 14.04上得到sh: 1: Bad substitution,除非我用bash替换sh,见这里。
-
@GoZoner哦,${file/foo/bar}是"shell参数扩展"技术吗?
shell循环中的git mv?
git-mv的目的是什么?
(假设你在一个有合理外壳的平台上!)
在@ jonathan-camenish的答案基础上:
1 2 3 4 5 6 7 8 9 10
| # things between backticks are 'subshell' commands. I like the $() spelling over ``
# git ls-files -> lists the files tracked by git, one per line
# | grep somestring -> pipes (i.e.,"|") that list through a filter
# '|' connects the output of one command to the input of the next
# leading to: for file in some_filtered_list
# git mv f1 f2 -> renames the file, and informs git of the move.
# here 'f2' is constructed as the result of a subshell command
# based on the sed command you listed earlier.
for file in `git ls-files | grep filenamematch`; do git mv $file `echo $file | sed -e 's/%filenamematch%/%replacement%/'`; done |
这是一个较长的例子(在bash或类似的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| mkdir blah; cd blah;
touch old_{f1,f2,f3,f4} same_{f1,f2,f3}
git init && git add old_* same_* && git commit -m"first commit"
for file in $(git ls-files | grep old); do git mv $file $(echo $file | sed -e 's/old/new/'); done
git status
# On branch master
# Changes to be committed:
# (use"git reset HEAD <file>..." to unstage)
#
# renamed: old_f1 -> new_f1
# renamed: old_f2 -> new_f2
# renamed: old_f3 -> new_f3
# renamed: old_f4 -> new_f4
# |
另请参见:Unix命令行中的Ad Hoc数据分析
-
基本上,无论你在哪里使用mv,都要使用git mv。如果您需要更清晰,请使用一些示例重命名更新您的问题。
-
更新帖子也许现在更清楚了,有什么想法吗?我对bash脚本不好。
-
我希望扩展的例子有所帮助!
-
我真的认为这是对这个问题的正确答案。 GIT能够跟踪文件移动。没有专有的复制脚本。谢谢!
这适用于我的用例:
1
| ls folder*/*.css | while read line; do git mv -- $line ${line%.css}.scss; done; |
说明:
-
ls folder*/*.css - 使用ls获取具有与glob模式匹配的CSS文件的所有目录的列表。 (以folder开头并包含.css扩展名的文件的目录)
-
while read line - 逐行读取ls命令的结果输出
-
do git mv -- $line ${line%.css}.css - 在逐行输出($line变量包含每一行)上执行git mv,同时匹配每个文件名的开头并排除.css扩展名(使用${line%并添加新的 extension(--用于防止文件名和标志之间的歧义)
下面的代码可用于"干运行"(实际上不会执行git mv):
1
| ls variant*/*.css | while read line; do echo git mv $line to ${line%.css}.scss; done; |
我用自己解决了这个问题
https://github.com/75lb/renamer - 完美地工作:-)
没有明确地做git mv但git似乎完全处理结果。
当我按照最高答案中的所有步骤操作时,我在最后一步卡住了,当我的控制台响应时
for pipe quote>
如果有人能解释这一点,我会很感激!
我通常使用NetBeans来执行此类操作,因为当有更简单的方法时我会避开命令行。 NetBeans对git有一些支持,您可以通过"收藏夹"选项卡在任意目录/文件中使用它。