Bash如何重命名一系列软件包以删除其版本号?我一直在expr
和和玩弄,都%%
没有用。
例子:
Xft2-2.1.13.pkg
变成 Xft2.pkg
jasper-1.900.1.pkg
变成 jasper.pkg
xorg-libXrandr-1.2.3.pkg
变成 xorg-libXrandr.pkg
Bash如何重命名一系列软件包以删除其版本号?我一直在expr
和和玩弄,都%%
没有用。
例子:
Xft2-2.1.13.pkg
变成 Xft2.pkg
jasper-1.900.1.pkg
变成 jasper.pkg
xorg-libXrandr-1.2.3.pkg
变成 xorg-libXrandr.pkg
Answers:
您可以使用bash的参数扩展功能
for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done
带空格的文件名需要使用引号。
rename "s/-[0-9.]*//" *.pkg
如果所有文件都在同一目录中,则序列
ls |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
会做你的工作。的sed的命令将创建的序列MV命令,这些命令可以然后管进入外壳。最好先运行管道,不要拖尾| sh
,以验证该命令是否满足您的要求。
要遍历多个目录,请使用类似
find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh
请注意,在sed中,正则表达式分组顺序是方括号前加反斜杠 \(
和\)
,而不是单个方括号(
和)
。
ls
在脚本中使用。第一个品种可以通过printf '%s\n' * | sed ...
创造力来实现,并且您也可以摆脱一些创造力sed
。Bash printf
提供了一种'%q'
格式代码,如果您的文件名包含文字壳元字符,它可能会派上用场。
我会做这样的事情:
for file in *.pkg ; do
mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done
假设您的所有文件都在当前目录中。如果不是,请尝试使用Javier上面建议的find。
编辑:而且,此版本不使用任何特定于bash的功能,就像上面的其他功能一样,这使您具有更大的可移植性。
这是POSIX几乎等同于当前接受的答案。这将仅Bash ${variable/substring/replacement}
参数扩展替换为在所有Bourne兼容shell中都可用的参数扩展。
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
参数扩展${variable%pattern}
产生variable
带有匹配的所有后缀的值pattern
。(还${variable#pattern}
可以删除前缀。)
-[0-9.]*
尽管可能会引起误解,但我还是拒绝了该子模式。它不是一个正则表达式,而是一个全局模式。因此,这并不意味着“短划线后跟零个或多个数字或点”。相反,它的意思是“一个破折号,后跟一个数字或一个点,后跟任何东西”。“一切”将是最短的匹配,而不是最长的匹配。(Bash提供##
并%%
用于修剪可能的最长前缀或后缀,而不是最短的前缀。)
我们可以假定sed
在任何* nix上都可以使用它,但是我们不能确定它是否支持sed -n
生成mv命令。(注意:只有GNU sed
这样做。)
即便如此,bash内置函数和sed还是可以快速启动shell函数来完成的。
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
mv -v "$file" "$(sed $sed_pattern <<< $file)"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.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
由于mv
不会自动创建目标文件夹,因此我们无法使用的初始版本sedrename
。
这是一个很小的更改,因此最好包含该功能:
我们将需要一个实用函数abspath
(或绝对路径),因为bash没有内置此函数。
abspath () { case "$1" in
/*)printf "%s\n" "$1";;
*)printf "%s\n" "$PWD/$1";;
esac; }
一旦有了,就可以为sed / rename模式生成目标文件夹,其中包括新的文件夹结构。
这将确保我们知道目标文件夹的名称。重命名时,需要在目标文件名上使用它。
# 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"
这是完整的文件夹感知脚本...
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
target="$(sed $sed_pattern <<< $file)"
mkdir -p "$(dirname $(abspath $target))"
mv -v "$file" "$target"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
当然,当我们也没有特定的目标文件夹时,它仍然可以工作。
如果我们想将所有歌曲放入一个文件夹,./Beethoven/
我们可以这样做:
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
使用此脚本将文件从文件夹移动到单个文件夹:
假设我们想收集所有匹配的文件,并将它们放置在当前文件夹中,我们可以这样做:
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
常规sed模式规则适用于此脚本,这些模式不是PCRE(与Perl兼容的正则表达式)。您可以使用sed扩展正则表达式语法,sed -r
或者sed -E
根据您的平台使用。
有关man re_format
sed基本和扩展正则表达式模式的完整说明,请参阅POSIX兼容。
我发现重命名是用于这种事情的更直接的工具。我在Homebrew for OSX上找到了它
以您的示例为例:
rename 's/\d*?\.\d*?\.\d*?//' *.pkg
“ s”表示替代。格式为s / searchPattern / replacement / files_to_apply。您需要为此使用正则表达式,这需要进行一些研究,但是值得付出努力。
for
和sed
等等是好的,但它更简单,快捷,只需使用rename
如果你发现自己经常这样做。
rename
命令;另外,某些命令将具有不同的 rename
命令,这些命令具有不同的功能,语法和/或选项。看起来很rename
流行的Perl 。但是答案确实可以澄清这一点。
更好地sed
用于此目的,例如:
find . -type f -name "*.pkg" |
sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
while read nameA nameB; do
mv $nameA $nameB;
done
找出正则表达式作为练习(就像处理包含空格的文件名一样)
假设
剥离.pkg,然后剥离-..
for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
-e
。例如sed -e 's/\.pkg//g' -e 's/-.*//g'
。
我有多个*.txt
文件要重命名为.sql
同一文件夹中。下面为我工作:
for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done
感谢您的回答。我也有一些问题。将.nzb.queued文件移动到.nzb文件。文件名中包含空格和其他内容,这解决了我的问题:
find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh
它基于Diomidis Spinellis的答案。
正则表达式为整个文件名创建一组,为.nzb.queued之前的部分创建一组,然后创建一个shell移动命令。用字符串引用。这也避免了在shell脚本中创建循环,因为这已经由sed完成。
-n
相同的方式解释该选项。