Linux shell脚本查找和重命名文件以移除后缀?

3
我有一些文件,文件名如下:
file1.c.keep.apple file2.c.keep.apple 我想编写一个shell脚本,以使我可以通过参数传递后缀(在这种情况下是apple),它将重命名所有文件并删除.keep.apple
示例执行: script.sh apple 结果将导致上述文件被重命名为
file1.c
file2.c
到目前为止,我已经:
 #! /bin/sh
 find . -type f -name \'*.keep.$1\' -print0 | xargs -0 rename 's/\(.keep.*)$//'

文件没有被重命名。我知道 find 部分是正确的。我认为我的重命名正则表达式有误。如何使脚本按照我想要的方式工作?


需要递归地遍历多个子目录吗? - jesse_galley
是的,抱歉。我希望当我从一个目录执行这个脚本时它能够工作,并且搜索当前目录和所有子目录。 - boltup_im_coding
请检查我的修改后的答案,它似乎可以满足你的需求。 - jesse_galley
根据您的需求和Uwe Kleine-König的评论,我再次更新了我的答案。您应该再次查看答案和评论! - jesse_galley
6个回答

4

我知道 find 的部分是正确的。

除了它并不正确。

find . -type f -name "*.keep.$1" -print0 | ...

我将其设置为\'*.keep.$1\',以便$1被识别为参数。使用您的解决方案,它不会在文件名中寻找字符串文字$1吗? - boltup_im_coding
2
不,它不是。作为额外的奖励,它不会让$1中的空格破坏您的系统。 - Ignacio Vazquez-Abrams
哦,有趣。谢谢。不管怎样,因为xargs部分仍然有错误,所以即使您更改了脚本,它仍然无法正常工作。 - boltup_im_coding
1
你的括号不平衡。 - Ignacio Vazquez-Abrams
使用 \'*.keep.$1\' 可以搜索以 ' 开头的文件名。 - Uwe Kleine-König
这个“答案”看起来像是一条评论。 - Henke

3
更新了,也许可以试试这个:
#!/bin/bash

SUFFIX=$1;

find . -type f -name "*keep.${SUFFIX}" | while read -r file;
do 
    nfile=`echo $file | sed "s/\.keep\.${SUFFIX}//g"`; 
    mv "$file" "$nfile" 2>/dev/null; 
done

这里是运行结果:

jgalley@jgalley-debian:/test5$ cat replace.sh 
#!/bin/bash

SUFFIX=$1;

find . -type f -name "*keep.${SUFFIX}" | while read -r file;
do 
    nfile=`echo $file | sed "s/\.keep\.${SUFFIX}//g"`; 
    mv "$file" "$nfile" 2>/dev/null; 
done
jgalley@jgalley-debian:/test5$ find .
.
./-filewithadash.keep.apple
./dir1
./dir1/file
./dir1/file2.keep.orange
./dir2
./dir2/file2
./file with spaces
./file.keep.orange
./file.keep.somethingelse.apple
./file.orange
./replace.sh
jgalley@jgalley-debian:/test5$ ./replace.sh apple
jgalley@jgalley-debian:/test5$ find .
.
./-filewithadash
./dir1
./dir1/file
./dir1/file2.keep.orange
./dir2
./dir2/file2
./file with spaces
./file.keep.orange
./file.keep.somethingelse.apple
./file.orange
./replace.sh
jgalley@jgalley-debian:/test5$ 

1
这并没有解决“.keep”部分,而是使用“file”代替。另外请注意,如果文件名包含空格,则此方法无法正常工作(这可能很重要)。 - Uwe Kleine-König
我将find命令更改为“*.keep.$1”,sed更改为“s/.keep.${SUFFIX}//g”,现在它对我有效! - boltup_im_coding
1
@unexpected62:你想引用点并在sed表达式末尾添加$,得到s/\.keep\.${SUFFIX}$//。否则,对于像file.keep.applepie.keep.apple这样的文件名,您将获得有趣的结果。 - Uwe Kleine-König
我发现另一件可能不好的事情 :-) 如果您的文件名以“-”开头,则“echo”和“mv”命令可能无法正常工作。 - Uwe Kleine-König
@Uwe Kleine-König 破折号实际上不应该是问题,我已经更新了我的答案以在测试中显示这个。 - jesse_galley
显示剩余2条评论

1

我认为你需要:

find . -type f -name "*.keep.$1" -print0 | xargs -0 rename "s/\.keep\.$1$//"

请注意以下限制:
  • 重命名可能不是在所有地方都可用的。
  • find -print0xargs -0 是 GNU 扩展,可能不是在所有 Unix 系统上都可用。
  • 如果您的第一个参数包含正则表达式中特殊的字符,则结果可能与您想要的不同。(例如:yourscript "a*e"

@unexpected62:嗯,对我来说可以用(即使在你评论后进行了一些编辑,使命令更加健壮)。我执行了以下操作:mkdir adir; touch adir/file2.c.keep.apple; touch file1.c.keep.apple; set apple; find . -type f -name "*.keep.$1" -print0 | xargs -0 rename "s/\.keep\.$1$//" - Uwe Kleine-König
你的脚本中 $1 代表什么意思? - Registered User

1
如果您熟悉Bash,并且使用的是大于4版本(支持globstar),那么这里有一个纯Bash解决方案:
#!/usr/bin/env bash

(($#)) || exit 1

shopt -s globstar nullglob
for f in **/*.keep."$1"; do
    mv -- "$f" "${f%.keep.$1}"
done

或者,这里有一个使用findwhile read循环的解决方案(假设使用GNU或BSD find):

find . -type f -name "*.keep.$1" -print0 | while IFS= read -r -d '' f; do
    mv -- "$f" "${f%.keep.$1}"
done

请参阅http://mywiki.wooledge.org/BashFAQ/030以获取有关此解决方案的更多详细信息。

此外,您还可以使用find-exec来实现您要尝试的操作:

find . -type f -name "*.keep.$1" -exec sh -c 'mv -- "$2" "${2%.keep.$1}"' _ "$1" {} ';'

在最后的 find 解决方案中:在执行语句中,您可能意味着 bash -c 'mv -- "$1" "${1%.keep.$0}"' "$1" {}。如果 $1* 或者 $1;,它会以某种方式失败。 - gniourf_gniourf
不,我的意思是我打的那样。我写的和你建议的唯一区别是使用了“bash”,以及对位置参数进行了移动。“bash”并不必要,因为POSIX sh支持参数扩展;你对位置参数的移动也是不必要的,也不相关。实际上,在后缀中包含“*”字符也完全可以正常工作,因为当参数在双引号内时,shell不会执行glob扩展。 - Josh Cartwright
我在网上搜索一些不清楚的东西时遇到了这个解决方案,其中使用了“mv --”$2“”$ {2%.keep.$1}“。请问${2%.keep.$1}和$2代表什么?另外,在您的解决方案中使用下划线“$1”{},然后使用“;”是什么语法? - Registered User
${2%.keep.$1}是一个参数扩展(请参阅bash手册);此PE将扩展为删除后缀.keep.$1$2的值(在此之前,会先扩展$1)。为了更好地理解这里如何设置$1$2,有助于了解sh -c的工作原理。例如,sh -c 'echo "$1"' _ "hello, world!"将生成一个shell来运行'echo "$1"'命令字符串,并将位置参数设置为:$0=_;$1="hello, world!"(下划线是占位符$0)。其余部分都是用find包装起来。希望这可以帮助您! - Josh Cartwright

1

这个怎么样?

[spatel@us tmp]$ x=aa.bb.cc
[spatel@us tmp]$ y=${x%.cc}
[spatel@us tmp]$ echo $y
aa.bb


[spatel@us tmp]$ x=aa.bb.cc
[spatel@us tmp]$ y=${x%.bb.cc}
[spatel@us tmp]$ echo $y
aa

太棒了!我用Linux已经有26年了,但是我还不知道这个。更多详情请点击这里 - undefined

0

如果你可以简单地使用glob函数来获取文件,那么你就可以做到这一点。

rename '.keep.apple' '' *

否则,您将使用已经拥有的find+xargs替换*

请注意,在此处的rename是指来自util-linuxrename。在某些系统上,它被安装为rename.ul而不是rename


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