Bash:对于文件名执行的操作

4

你好,我不太熟悉Bash,所以遇到了一些问题:

我的目标是在一个文件夹中查找特定的文件,如果找到,则生成一些具有相同文件名但不同扩展名的其他文件。

类似于以下内容:

For Files-that-are-called-"SysBackup*.Now" 
do
  NewFileName = ChangeFileExt(FoundFilename),BK
  GenerateNewfile(NewFileName)
done

当然,上面的代码只是虚拟的,我不会用我写过但无法工作的代码来烦扰你 :-)

所以目标应该是:

如果文件夹中包含Sysbackup123.now和Sysbackup666.now,则最终会得到文件Sysbackup123.bk和Sysbackup666.bk。

感谢您的帮助。

迈克尔

5个回答

8
相当简单:
for a in Sysbackup*.now;
do
  [ -f $a ] && touch $(basename $a .now).bk ;
done

Bash还允许使用*%从字符串末尾删除字符。而且你可能应该养成引用文件名的习惯:*touch "${a%.now}.bk" - NVRAM
嗯,有一件事:for file in Sysbackup*.now;do echo "WTF?" done问题是每次运行我的脚本时,“WTF?”都会被写入屏幕,即使没有Sysback.now文件。我猜测(也需要)该函数仅在实际存在Sysbackup*.now文件的情况下才运行。 - Reiler
@Reiler:如果没有匹配的文件,那么字面搜索模式将被赋值给a。因此,你需要在touch命令前加上一个测试,比如[ -e $F ] && ,甚至更具体一些的[ -f $F ] && 。请参考下面的答案中的示例。 - Stephan202
我在touch命令之前添加了对文件的检查。(我最初确实有那行代码,但后来把它删掉了!唉!) - Stephen Darlington
非常感谢,检查它是否是文件完成了任务。 - Reiler
Reiler,请记得点击勾选此回答的复选框,以便系统知道它是被接受的答案。 - Lars D

2
这是我使用 sh 实现的方法:
#!/bin/sh

for file in SysBackup*.now; do
  if [ ! -e "$file" ]; then
    continue
  fi

  base=${file##*/}
  bk=${base%.now}.bk

  touch $bk
done

虽然我不认为这很有趣,但是我使用的解决方案是否更快,因为它不需要分叉和调用“basename”? - Gregory Pakosz

2

以下是另一种替换的方法。Bash有一个相当于sed的s/string1$/string2/的等效命令,它使用正则表达式中的美元符号来选择模式的末尾:

for a in Sysbackup*.Now;
do
  [ -f $a ] && touch "${a/%.Now/.BK}"
done

百分号(%)匹配字符串结尾,井号(#)匹配字符串开头。

你需要在第一行使用 .now:如果匹配文件,它们将以 .now 结尾而不是 OP 所用的 .Now,也不是你在变量模式中使用的 .Now -- 因此,你只会 touch 原始文件。 - NVRAM
正如我对 ghostdog74 所说的 - 这就是我通常避免使用 替换(substitute) 模式,而改用 删除/追加(remove/append) 的一个原因:**${file%.Now}.BK** -- 这永远不会与原文件名相同。一次我做了这样的事情:grep "$PATTERN" "$a" > "${a/%.Now/.BK}" -- 结果导致所有数据丢失。 - NVRAM

1
你可以像这样做:
for F in SysBackup*.Now; do
    [ -f $F ] && touch $(echo "$F" | sed 's/Now$/BK/')
done

这里使用echo命令将文件名传递给sed命令,用于将后缀字符串"Now"替换为"BK"touch命令用于创建文件(如果不存在的话),新文件将为空。测试[ -f $F ]确保只考虑文件而非目录或符号链接。

或者,您也可以使用find命令。以下是一个示例,使用basename命令,如Stephen Darlington's answer中所演示的:

find . -type f -iname "SysBackup*.Now" -exec sh -c "touch \$(basename '{}' .Now).BK" \;

请注意,在这两个示例中,重要的是在文件名周围放置引号,以确保正确处理带有空格的文件名。

0

这里有另一种方式,如果您不想使用for循环和测试文件是否是文件的方法

find /path -maxdepth 1 -type f -name "Sysbackup*now" | while read file
do
  touch "${file/%.now/.bK}"
done

不确定我是否喜欢使用find仅仅是为了避免无匹配问题,但无论如何。然而,在你的*-name参数中需要一个点,否则将找到一个名为Sysbackup-before-snow的文件,并且由于它没有以“.now”结尾,文件名将不变地传递给touch程序。这就是我通常避免使用substitute模式并改用remove/append的原因之一:${file%.now}.bk*--它永远不会与原始文件名相同(至少会附加一个“.bk”)。 - NVRAM

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