使用sed和mv重命名文件

13

我想要以以下格式重命名文件:

img_MM-DD-YY_XX.jpg

img_MM-DD-YY_XXX.jpg

改为:

newyears_YYYY-MM-DD_XXX.jpg

其中:

  • YYYY = 年份
  • MM = 月份
  • DD = 日期
  • XXX 或 XX = 照片编号

我编写了以下脚本,但它没有生效:

for filename in ?*.jpg; do
        newFilename=$(echo $filename | \
        sed 's/img_\(.*\)-\(.*\)-\(.*\)_\([0-9][0-9]\)\./newyears_20\3-\1-\2_0\4./;
                s/img_\(.*\)-\(.*\)-\(.*\)_\([0-9][0-9][0-9]\)/newyears_20\3-\1-\2_\4/' -)
        mv $filename $newFilename
done

非常感谢您的帮助。


当您运行脚本时会发生什么?for filename in ?*.jpg; do echo $filename; done 返回什么? - justarobert
我找到了问题所在。在管道符“|”之后删除反斜杠“\”,就可以正常工作了。我猜在Bash脚本中不应该转义换行符。这个说法正确吗? - tiagob
反斜杠不是必需的,但不应该引起任何问题。它后面可能有一个空格或类似的东西吗? - Gordon Davisson
7个回答

17

你可以在bash中尝试这个脚本:

for filename in *.jpg; do
  newFilename=$(sed -E 's#img_([0-9]{1,2})-([0-9]{1,2})-([0-9]{1,2})_(.*)$#newyears_20\3-\2-\1_\4#' <<< "$filename")
  mv "$filename" "$newFilename"
done

sed -E 也被 GNU sed 支持。


1
你也想在FreeBSD上使用-E。 - Lucas Holt
1
不错。如果$filename和$newFilename中有空格,可以在它们周围添加双引号。 - shiggity

3

不使用for循环。

ls | grep 'jpg$' | sed '
#Save the original filename
h
#Do the replacement
s/img_\(.*\)-\(.*\)-\(.*\)_\([0-9][0-9]\)\./newyears_20\3-\1-\2_0\4.//
s/img_\(.*\)-\(.*\)-\(.*\)_\([0-9][0-9][0-9]\)/newyears_20\3-\1-\2_\4//
#Bring the original filename back
x
G
s/^\(.*\)\n\(.*\)$/mv "\1" "\2"' | bash

省略管道符号到bash以在mv之前查看结果

感谢http://www.gnu.org/software/sed/manual/sed.html#Rename-files-to-lower-case


不要解析 ls 输出 - Gilles Quénot
@GillesQuenot 你可以给一个原因吗?这会很有帮助的。 - earthdan

1

这个简单的变体对我有效:

$ cat mapper
for filename in ?*.jpg
do
    newFilename=$(echo $filename | \
    sed -e 's/img_\(.*\)-\(.*\)-\(.*\)_\([0-9][0-9]\)\./newyears_20\3-\1-\2_0\4./' \
        -e 's/img_\(.*\)-\(.*\)-\(.*\)_\([0-9][0-9][0-9]\)/newyears_20\3-\1-\2_\4/')
    echo mv $filename $newFilename
done
$ echo > img_04-23-09_123.jpg
$ echo > img_08-13-08_33.jpg
$ sh mapper
mv img_04-23-09_123.jpg newyears_2009-04-23_123.jpg
mv img_08-13-08_33.jpg newyears_2008-08-13_033.jpg
$

唯一的区别是使用显式的-e选项代替分号。

在MacOS X 10.6.7上测试通过。


似乎太脆弱了。需要引用。 - Gilles Quénot

0

我记得曾经玩过这种东西。我发现创建一个脚本来执行你想要的操作通常很有用:

例如,你的脚本输出可能是一个文件,如下:

mv   file1 file2
mv   file3 file4
.....
mv   fileN fileM

要创建这个,请执行 ls | grep 日期模式 | sed 将文件1移动到文件2的脚本 > myscript

然后只需执行 ./myscript

这样你就可以更好地查看中间输出,找出问题所在。


0

这可以通过一个纯 bash 脚本来处理。

for j in ?*.jpg
do
  n="newyears_20${j:10:2}-${j:4:2}-${j:7:2}";
  if [ ${j:15:1} = "." ];then
    n="${n}_0${j:13}"
  else
    n="${n}${j:12}"
  fi
  mv $j $n
done

似乎太脆弱了。需要引用。 - Gilles Quénot

-1

Ruby(1.9+)

$ ruby -e 'Dir["img*jpg"].each{|x|File.rename(x,x.gsub(/img_(.*?)-(.*?)-(.*?)\.jpg/,"newyears_20\\2-\\1-\\3.jpg") )}'

-1
for f in *.jpg; do
  n=$(sed -r 's/^(.+)-([^_]+)_(.+)\.jpg/\2-\1_\3.jpg/' <<< "${f#img_}")
  mv "$f" "newyears_20$n"
done

<<< 是一个名为 here stringbash 特性,${f#img_}$f 中删除了前缀 img_。这个 sed 表达式将 img_MM-DD-YY_XX.jpg 转换成 YY-MM-DD_XX.jpg,通过将 MM-DD 分离到 \1,将 YY 分离到 \2,将 XX 分离到 \3,然后形成 \2-\1_\3.jpg。请注意,在 n 赋值中不需要引用。

为了安全起见,对于这样的事情,我实际上更喜欢使用 echo mv 而不是 mv,这样我可以看到它将生成的命令。然后您可以删除 echo 或将输出导入到 sh


似乎太脆弱了。需要引用。 - Gilles Quénot
1
请在您的答案中添加解释,以便长期价值和未来访问者可以从您的解决方案中学习,并将此知识应用于自己的编码问题。仅代码答案会受到不良反响,并且不太可能获得赞同。 - SherylHohman
修正了引用问题,并添加了解释。实际上,我重写了整个代码,因为输出结果完全错误。我想当时没有进行测试。 - Idelic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接