bash:递归处理目录中的所有文件

7

我想写一个bash脚本,用于(递归地)处理所有特定类型的文件。

我知道可以通过以下方式使用find获取匹配的文件列表:

find . -name "*.ext"

我想在脚本中使用它:

  1. 递归地获取具有给定扩展名的文件列表
  2. 获取完整的文件路径名
  3. 将完整路径名传递给另一个脚本
  4. 检查脚本的返回代码。如果非零,则记录无法处理的文件名。

我的第一次尝试看起来像这样(伪代码):

ROOT_DIR = ~/work/projects
cd $ROOT_DIR
for f in `find . -name "*.ext"`
do
    #need to lop off leading './' from filename, but I havent worked out how to use
    #cut yet
    newname = `echo $f | cut -c 3
    filename = "$ROOT_DIR/$newname"

    retcode = ./some_other_script $filename

    if $retcode ne 0
       logError("Failed to process file: $filename")
done

这是我第一次尝试编写bash脚本,因此上面的代码片段可能无法运行。但希望我的逻辑足够清晰,有人能够指导如何连接各个部分并将上述伪代码转换为可工作的脚本。

我正在运行Ubuntu操作系统。


1
如果您使用“find $ROOT_DIR -name“*.exe””,则无需调整前导的'./'。 - martin clayton
2个回答

16
find . -name '*.ext' \( -exec ./some_other_script "$PWD"/{} \; -o -print \)

+1 好的解决方案,我不知道你可以这样使用 -o。唯一的问题是有时候(不是这里)你需要调用一个函数而不是一个外部可执行文件。 - tokland
这有点晦涩难懂...(让我想起了Perl!)。我不理解这个。文件名是如何传递给其他脚本的,我怎么知道脚本是否返回了非零值,以便我知道要做些什么? - skyeagle
1
@skyeagle:-exec谓词中的{}表示注入当前匹配项的位置。-exec谓词本身测试命令的结果代码,如果为0则返回true,否则返回false。 - Ignacio Vazquez-Abrams

2

使用| while read来遍历文件名是可以的,只要没有需要处理的带回车符的文件:

find . -name '*.ext' | while IFS=$'\n' read -r FILE; do
  process "$(readlink -f "$FILE")" || echo "error processing: $FILE"
done

1
我不会说它被正式地反对了。它比 for f in $(find) 好得多。如果文件名中有空格、制表符或换行符,则可能会出现问题。可以像这样改进:while IFS=$'\n' read -r FILE - Dennis Williamson
@Dennis,我现在找不到那个页面了(可能不是很正式),但它不鼓励使用“while | read”,因为回车符是文件中有效的字符(幸运的是,没有人使用它们)。已添加您的建议。 - tokland
这样的假设总是有问题的。即使“普通”用户经常避免在文件名中使用换行符,也并不意味着没有人这样做——攻击者肯定会这样做。任何程序都应该尽力接受所有允许的文件名;幸运的是,在每种真正的编程语言中都很容易实现这一点——这也是避免使用Shell脚本的另一个原因。 - Philipp
1
也许你在想这个页面:http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html。它是一个关于文件名所带来挑战的很好的讨论。 - Dennis Williamson
@tokland:我同意shell语言很有用,但是它们也被高估了,正如这个问题再次显示的那样。Bash和其他shell的最大障碍是“函数”(即程序)仅在字符串上工作,您必须设计自定义序列化方案来实现更复杂的数据结构。其他语言中的find等价物可以返回数组或迭代器。像PowerShell这样的现代shell已经承认了这个缺陷,并允许传递和返回任意对象。 - Philipp
显示剩余2条评论

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