如何使用Posix shell删除文件名前缀

46

我该如何在Bash中去掉文件名前缀,就像以下示例:

XY TD-11212239.pdf

获得

11212239.pdf

即,删除XY TD-


4
Linux是一个内核。你实际上是指[bash]还是[sh](或其他shell)? - ArjunShankar
@Shahbaz:嗯...但是OP说XY TD-是“文件名前缀”,他想要删除它。那么“XY”可能不是一个shell命令。 - ArjunShankar
@hamid - 你想重命名文件并删除前缀吗?你想在像bash这样的shell中执行还是在程序中执行(使用哪种编程语言)? - ArjunShankar
Bash是POSIX sh的超集。你想要Bash还是想要POSIX sh? - Charles Duffy
我认为答案取决于您还想重命名哪些其他文件。例如,如果您有一堆文件,并且对于每个文件名,您想删除第一个破折号(-)及其之前的所有字符,则尝试 for file in *; do echo mv "$file" "${file#*-}"; done。当然,在预览看起来不错时,请删除 echo。这类似于这个答案 - Henke
显示剩余2条评论
5个回答

56

你说了POSIX shell,这将包括BASH、Kornshell、Ash、Zsh和Dash。幸运的是,所有这些shell对变量的值都执行模式过滤

当你在Unix/Linux命令行上使用像*这样的东西指定文件时,就会用到模式:

$ ls *.sh  # Lists all files with a `.sh` suffix

这些 POSIX shell 使用四种不同的模式过滤方式:

  • ${var#pattern} - 从左侧删除最小匹配模式的字符串。
  • ${var##pattern} - 从左侧删除最大匹配模式的字符串。
  • ${var%pattern} - 从右侧删除最小匹配模式的字符串。
  • ${var%%pattern} - 从右侧删除最大匹配模式的字符串。

以下是一些示例:

foo="foo-bar-foobar"
echo ${foo#*-}   # echoes 'bar-foobar'  (Removes 'foo-' because that matches '*-')
echo ${foo##*-}  # echoes 'foobar' (Removes 'foo-bar-')
echo ${foo%-*}   # echoes 'foo-bar'
echo ${foo%%-*}  # echoes 'foo'

你没有清楚地解释你想要什么,并且没有包含任何代码示例,所以很难想出能够做到你想要的东西。但是,通过使用模式过滤,您可能可以确切地找出您想要对文件名执行的操作。

file_name="XY TD-11212239.pdf"
mv "$file_name" "${file_name#*-}" # Removes everything from up to the first dash

32
您提供的信息很少,但是假设您正在bash中执行此操作,并且有一个需要删除前缀的文件列表,您可以通过sed管道传递该列表进行操作:

例如:

./generate_your_list_of_files | sed -e 's/^prefix//'

如果您的文件列表是以空格分隔的:
echo TD-file1 TD-file2.x.yt TD-file3-beep | sed -e 's/\<TD-//g'

第一个匹配项与行首相匹配并删除它。第二个匹配项仅在单词开头出现时匹配 TD-(或任何其他您想要替换的前缀),并在所有行中的所有匹配项中替换它。然而,这可能会变得危险,例如像TD-file\ that\ TD-contains\ space.txt这样的文件将变成file\ that\ contains\ space.txt

顺便说一下,请不要使用ls来获取文件列表。那是一个可怕的错误。如果您需要获取要处理并在多个地方引用的文件列表,我建议将它们放入数组中:

files=(*)

然后与这个数组一起工作。


由于受欢迎的请求(下面的单个评论),这里是如何重命名以 XY TD- 开头的目录中的所有文件,使前缀被删除(感谢 @tripleee):
for file in prefix*;
do
    mv "$file" "${file#XY TD-}"
done

@tripleee,谢谢你让我知道。实际上,我最近才开始学习bash的脚本语言。我已经更新了我的答案。 - Shahbaz
2
为了提高效率和正确性,可以使用适当的引用和Shell内置替换机制,使用以下命令 mv "$file" "${file#XY\ TD-}" - tripleee
@Shahbaz,你是否考虑从你的答案中删除不良做法,或将它们移到最后以突出tripleee建议的最佳实践方法?当这个答案所强调的是被认为有害的做法时,很难支持它。 - Charles Duffy
@CharlesDuffy,我避免使用ls。我只是为了让新手感到熟悉,并稍后提醒他们注意。但这并不是非常重要的。 - Shahbaz
它没有起作用:files=(*);echo $files|sed -e 's/^new.//'。为什么?我使用zsh。 - Timo
显示剩余6条评论

18
您也可以使用重命名工具。
使用homebrew进行安装: brew install rename 然后在目录中运行下面的命令,从所有文件中删除“prefix-”前缀: rename -d prefix- *

3
好用的、强大的 [标签:perl] 工具! - F. Hauri - Give Up GitHub

6

除了其他回答中展示的子字符串提取外,您还可以利用子字符串替换。给定文件:

fn="XY TD-11212239.pdf"

接下来是对子字符串的替换:

fn_new=${fn//*TD-/}

还可以做你想要的事情。


这个今天拯救了我的理智。谢谢。 - Translunar

2
$ s='XY TD-11212239.pdf'
$ echo "${s#XY TD-}"
11212239.pdf

如果您的目标是进行批量重命名:
while IFS= read -r -d '' filename; do
  mv "$filename" "${filename#XY TD-}"
done < <(find . -type f -name 'XY TD-*' -print0)

请注意,<()是bash的扩展,不包含在POSIX sh中。您可以使用形式为find ... | while的管道替换它。
同样,-print0是GNU扩展,不包含在POSIX find中。POSIX find没有等效的方法来定位文件,这对于包含换行符的名称是安全的,因此很难进行替换。

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