使用 Bash 批量重命名文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/602706/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Batch renaming files with Bash
提问by Jeremy L
How can Bash rename a series of packages to remove their version numbers? I've been toying around with both expr
and %%
, to no avail.
Bash 如何重命名一系列包以删除它们的版本号?我一直在玩弄expr
和%%
,但无济于事。
Examples:
例子:
Xft2-2.1.13.pkg
becomes Xft2.pkg
Xft2-2.1.13.pkg
变成 Xft2.pkg
jasper-1.900.1.pkg
becomes jasper.pkg
jasper-1.900.1.pkg
变成 jasper.pkg
xorg-libXrandr-1.2.3.pkg
becomes xorg-libXrandr.pkg
xorg-libXrandr-1.2.3.pkg
变成 xorg-libXrandr.pkg
回答by richq
You could use bash's parameter expansion feature
您可以使用 bash 的参数扩展功能
for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done
Quotes are needed for filenames with spaces.
带空格的文件名需要引号。
回答by Diomidis Spinellis
If all files are in the same directory the sequence
如果所有文件都在同一目录中,则序列
ls |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "" ".pkg"/p' |
sh
will do your job. The sedcommand will create a sequence of mvcommands, which you can then pipe into the shell. It's best to first run the pipeline without the trailing | sh
so as to verify that the command does what you want.
会做你的工作。的sed的命令将创建的序列MV命令,这些命令可以然后管进入外壳。最好先运行没有尾随的管道| sh
,以验证该命令是否满足您的要求。
To recurse through multiple directories use something like
要通过多个目录递归使用类似
find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "" ".pkg"/p' |
sh
Note that in sedthe regular expression grouping sequence is brackets preceded by a backslash, \(
and \)
, rather than single brackets (
and )
.
请注意,在sed 中,正则表达式分组序列是括号前面是反斜杠 \(
和\)
,而不是单个括号(
和)
。
回答by Diego Sevilla
I'll do something like this:
我会做这样的事情:
for file in *.pkg ; do
mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done
supposed all your file are in the current directory. If not, try to use find as advised above by Javier.
假设您的所有文件都在当前目录中。如果没有,请尝试按照 Javier 上面的建议使用 find 。
EDIT: Also, this version don't use any bash-specific features, as others above, which leads you to more portability.
编辑:此外,此版本不使用任何特定于 bash 的功能,如上面的其他功能,这使您具有更多的可移植性。
回答by tripleee
Here is a POSIX near-equivalent of the currently accepted answer. This trades the Bash-only ${variable/substring/replacement}
parameter expansion for one which is available in any Bourne-compatible shell.
这是当前接受的答案的 POSIX 近等价。这将 Bash-only${variable/substring/replacement}
参数扩展交换为在任何 Bourne 兼容 shell 中可用的参数扩展。
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
The parameter expansion ${variable%pattern}
produces the value of variable
with any suffix which matches pattern
removed. (There is also ${variable#pattern}
to remove a prefix.)
参数扩展${variable%pattern}
产生的值variable
与pattern
删除匹配的任何后缀。(还有${variable#pattern}
删除前缀。)
I kept the subpattern -[0-9.]*
from the accepted answer although it is perhaps misleading. It's not a regular expression, but a glob pattern; so it doesn't mean "a dash followed by zero or more numbers or dots". Instead, it means "a dash, followed by a number or a dot, followed by anything". The "anything" will be the shortest possible match, not the longest. (Bash offers ##
and %%
for trimming the longest possible prefix or suffix, rather than the shortest.)
我保留了-[0-9.]*
接受答案中的子模式,尽管它可能会产生误导。它不是一个正则表达式,而是一个 glob 模式;所以它并不意味着“破折号后跟零个或多个数字或点”。相反,它的意思是“一个破折号,后跟一个数字或一个点,后跟任何东西”。“任何”将是最短的匹配项,而不是最长的匹配项。(Bash 提供##
和%%
修剪尽可能长的前缀或后缀,而不是最短的。)
回答by ocodo
We can assume sed
is available on any *nix, but we can't be sure
it'll support sed -n
to generate mv commands. (NOTE:Only GNU sed
does this.)
我们可以假设sed
在任何 *nix 上都可用,但我们不能确定它是否支持sed -n
生成 mv 命令。(注意:只有 GNU 会这样做sed
。)
Even so, bash builtins and sed, we can quickly whip up a shell function to do this.
即便如此,使用 bash 内置函数和 sed,我们可以快速创建一个 shell 函数来执行此操作。
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=
shift
for file in $(ls $@); do
mv -v "$file" "$(sed $sed_pattern <<< $file)"
done
else
echo "usage: sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)||' *.pkg
before:
./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg
after:
./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg
sed_pattern files..."
fi
}
Usage
用法
abspath () { case "" in
/*)printf "%s\n" "";;
*)printf "%s\n" "$PWD/";;
esac; }
Creating target folders:
创建目标文件夹:
Since mv
doesn't automatically create target folders we can't using
our initial version of sedrename
.
由于mv
不会自动创建目标文件夹,因此我们无法使用sedrename
.
It's a fairly small change, so it'd be nice to include that feature:
这是一个相当小的变化,因此包含该功能会很好:
We'll need a utility function, abspath
(or absolute path) since bash
doesn't have this build in.
我们需要一个实用程序函数abspath
(或绝对路径),因为 bash 没有这个内置函数。
# generate the rename target
target="$(sed $sed_pattern <<< $file)"
# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"
# finally move the file to the target name/folders
mv -v "$file" "$target"
Once we have that we can generate the target folder(s) for a sed/rename pattern which includes new folder structure.
一旦我们有了它,我们就可以为包含新文件夹结构的 sed/rename 模式生成目标文件夹。
This will ensure we know the names of our target folders. When we rename we'll need to use it on the target file name.
这将确保我们知道目标文件夹的名称。当我们重命名时,我们需要在目标文件名上使用它。
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=
shift
for file in $(ls $@); do
target="$(sed $sed_pattern <<< $file)"
mkdir -p "$(dirname $(abspath $target))"
mv -v "$file" "$target"
done
else
echo "usage: sedrename 's|Beethoven - |Beethoven/|g' *.mp3
before:
./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3
after:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
sed_pattern files..."
fi
}
Here's the full folder aware script...
这是完整的文件夹感知脚本......
sedrename 's|.*/||' **/*.mp3
before:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
after:
./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3
Of course, it still works when we don't have specific target folders too.
当然,当我们也没有特定的目标文件夹时,它仍然有效。
If we wanted to put all the songs into a folder, ./Beethoven/
we can do this:
如果我们想把所有的歌曲放到一个文件夹中,./Beethoven/
我们可以这样做:
Usage
用法
rename 's/\d*?\.\d*?\.\d*?//' *.pkg
Bonus round...
奖金回合...
Using this script to move files from folders into a single folder:
使用此脚本将文件从文件夹移动到单个文件夹中:
Assuming we wanted to gather up all the files matched, and place them in the current folder, we can do it:
假设我们想收集所有匹配的文件,并将它们放在当前文件夹中,我们可以这样做:
find . -type f -name "*.pkg" |
sed -e 's/((.*)-[0-9.]*\.pkg)/ .pkg/g' |
while read nameA nameB; do
mv $nameA $nameB;
done
Note on sed regex patterns
关于 sed 正则表达式模式的注意事项
Regular sed pattern rules apply in this script, these patterns aren't
PCRE (Perl Compatible Regular Expressions). You could have sed
extended regular expression syntax, using either sed -r
or sed -E
depending on your platform.
正则 sed 模式规则适用于此脚本,这些模式不是 PCRE(Perl 兼容正则表达式)。您可以使用 sed 扩展正则表达式语法,sed -r
或者sed -E
根据您的平台使用。
See the POSIX compliant man re_format
for a complete description of
sed basic and extended regexp patterns.
有关man re_format
sed 基本和扩展正则表达式模式的完整描述,请参阅 POSIX 兼容。
回答by Simon Katan
I find that rename is a much more straightforward tool to use for this sort of thing. I found it on Homebrew for OSX
我发现重命名是用于此类事情的更直接的工具。我在 OSX 的 Homebrew 上找到了它
For your example I would do:
对于您的示例,我会这样做:
for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
The 's' means substitute. The form is s/searchPattern/replacement/ files_to_apply. You need to use regex for this which takes a little study but it's well worth the effort.
“s”表示替代。格式为 s/searchPattern/replacement/files_to_apply。您需要为此使用正则表达式,这需要一些研究,但值得付出努力。
回答by Javier
better use sed
for this, something like:
更好地sed
用于此,例如:
for i in \`ls *.txt | awk -F "." '{print }'\` ;do mv $i.txt $i.sql; done
figuring up the regular expression is left as an exercise (as is dealing with filenames that include spaces)
计算正则表达式留作练习(就像处理包含空格的文件名一样)
回答by Steve B.
This seems to work assuming that
这似乎工作假设
- everything ends with $pkg
- your version #'s always start with a "-"
- 一切都以 $pkg 结尾
- 您的版本号总是以“-”开头
strip off the .pkg, then strip off -..
剥离.pkg,然后剥离-..
find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\" \".nzb\"/p" |
sh
回答by Kumar Mashalkar
I had multiple *.txt
files to be renamed as .sql
in same folder.
below worked for me:
我有多个*.txt
文件要重命名为.sql
同一个文件夹。以下对我来说有效:
回答by Jerry Jacobs
Thank you for this answers. I also had some sort of problem. Moving .nzb.queued files to .nzb files. It had spaces and other cruft in the filenames and this solved my problem:
谢谢你的回答。我也遇到了一些问题。将 .nzb.queued 文件移动到 .nzb 文件。它的文件名中有空格和其他杂物,这解决了我的问题:
##代码##It is based on the answer of Diomidis Spinellis.
它基于 Diomidis Spinellis 的回答。
The regex creates one group for the whole filename, and one group for the part before .nzb.queued and then creates a shell move command. With the strings quoted. This also avoids creating a loop in shell script because this is already done by sed.
正则表达式为整个文件名创建一组,为 .nzb.queued 之前的部分创建一组,然后创建一个 shell 移动命令。用字符串引用。这也避免了在 shell 脚本中创建循环,因为这已经由 sed 完成了。