【问题标题】:Looping through files of specified extensions in bash在bash中循环指定扩展名的文件
【发布时间】:2014-03-19 02:48:53
【问题描述】:

我正在尝试使用 bash 脚本遍历指定扩展名列表的文件。我尝试了Matching files with various extensions using for loop 给出的解决方案,但它没有按预期工作。给出的解决方案是:

for file in "${arg}"/*.{txt,h,py}; do

这是我的版本:

for f in "${arg}"/*.{epub,mobi,chm,rtf,lit,djvu}
    do
        echo "$f"
    done

当我在一个包含epub 文件的目录中运行它时,我得到:

/*.epub
/*.mobi
/*.chm
/*.rtf
/*.lit
/*.djvu

所以我尝试更改for 声明:

for f in "${arg}"*.{epub,mobi,chm,rtf,lit,djvu}

然后我得到:

089281098X.epub
*.mobi
*.chm
*.rtf
*.lit
*.djvu

我也得到相同的结果:

for f in *.{epub,mobi,chm,rtf,lit,djvu}

所以看来"${arg}" 参数是不必要的。

虽然这些语句中的任何一个都能找到指定扩展名的文件并将它们传递给程序,但我从未解析的*. 文件名中得到读取错误。

我在OS X Mountain Lion 上运行这个。我知道默认的 bash shell 已经过时,所以我使用homebrew 将它从3.2.48 升级到4.2.45,看看这是否是问题所在。这没有帮助,所以我想知道为什么我会得到这些意想不到的结果。给定的解决方案是错误的还是OS X bash shell 与*NIX 版本有某种不同?是否有另一种方法可以在OS X bash shell 中完成同样的事情?

【问题讨论】:

  • 嗨。你能更详细地解释你想要做什么吗?由于代码已损坏,因此很难使用 that 作为您想要的描述。

标签: macos bash loops


【解决方案1】:

应该这样做:

for file in $(find ./ -name '*.epub' -o -name '*.mobi' -o -name '*.chm' -o -name '*.rtf' -o -name '*.lit' -o -name '*.djvu'); do
  echo $file
done

【讨论】:

  • -1 解析find 的输出几乎和bad as parsing ls 一样。如果找到的任何文件的名称中有空格,它将中断。另外,如果find 是正确答案,那么只需执行find ./ -name '*.epub' -name … 并让find 打印。
  • @kojiro 1. 你在开玩笑吗?那篇文章中提供的唯一解决方案是使用 glob。不完全是最灵活的解决方案,特别是如果您需要处理子目录。 2. OP 有一个 for 循环“无缘无故”去抱怨他们。我的操作是假设 for 循环对于他们实际上想要完成的任何事情都是必要的。
  • 我坚持我所说的——循环遍历分词命令替换很少是一个好主意。 1. glob 非常灵活,尤其是具有 nullglob、extglob 和 globstar 之类的扩展,但即使没有它们,OP 也不清楚是否要递归。 2. 同样,如果find 是正确答案,则整个答案可以用find 表示。如果find … -print 不对,find … -exec 就是。除此之外,find … -print0 | xargs -0 ….
【解决方案2】:

一个 glob 必须扩展为现有的找到的名称,否则它会保持原样并保留完整的星号。如果您有一个空目录,*.foo 将扩展为 *.foo。 (除非您使用 nullglob Bash 扩展。)

您的代码的问题在于您以 arg $arg 开头,这显然是空的或未定义的。因此,您的全局 ${arg}/*.epub 扩展为 /*.epub,因为根目录中没有以“.epub”结尾的文件。它永远不会在当前目录中查找。为此,您需要先设置arg=.

在您的第二个示例中,${arg}*.epub 确实会扩展,因为 $arg 是空的,但其他文件不存在,因此它们不会继续扩展为 glob。正如我之前所暗示的,一种简单的解决方法是使用shopt -s nullglob 激活nullglob。这是特定于 bash 的,但如果没有匹配的文件,将导致 *.foo 扩展为空字符串。对于严格的 POSIX 解决方案,您必须使用 [ -f "$f" ] 过滤掉未扩展的 glob。 (再说一次,如果你想要 POSIX,你也不能使用大括号扩展。)

【讨论】:

  • @David W.: shopt -s extglob for f in *.*(epub|mobi|chm|rtf|lit|djvu) 结果:089281098X.epub @kojiro:arg=. shopt -s nullglob for f in "${arg}"/*.{epub,mobi,chm,rtf,lit,djvu} 结果:./089281098X.epub shopt -s nullglob@987654在:089281098X.epub 所以所有这些变化都有效,但我不明白为什么。你们中的任何一个都可以解释每个变体发生了什么以及${arg} 在做什么吗?我真的很想了解这一点,所以我可以增加我的知识。感谢您的帮助。
  • 查看我原始答案的附件。
【解决方案3】:

这可能是 BASH 4.2 主义。它在我仍然是 3.2 的 BASH 中不起作用。但是,如果您 shopt -s extglob,则可以使用 *(...) 代替:

shopt -s extglob
for file in *.*(epub|mobi|chm|rtf|lit|djvu)
do
    ...
done

@David W.:shopt -s extglob for f in (epub|mobi|chm|rtf|lit|djvu) 结果:089281098X.epub @kojiro:arg=。 shopt -s nullglob for f in "${arg}"/.{epub,mobi,chm,rtf,lit,djvu} 结果:./089281098X.epub shopt -s nullglob for f in "${arg }".{epub,mobi,chm,rtf,lit,djvu} 结果:089281098X.epub 所以所有这些变体都有效,但我不明白为什么。你们中的任何一个都可以解释每个变体发生了什么以及 ${arg} 在做什么吗?我真的很想了解这一点,所以我可以增加我的知识。感谢您的帮助。

在我的:

for f in *.*(epub|mobi|chm|rtf|lit|djvu)

我没有包含${arg},它扩展为$arg 的值。 *(...) 匹配括号中的模式,该模式是任何一系列扩展之一。因此,它匹配*.epub

小次郎的:

arg=. 
shopt -s nullglob 
for f in "${arg}"/*.{epub,mobi,chm,rtf,lit,djvu}

在他的匹配中包括$arg 和斜线。因此,koriro 以./ 开头,因为这是他们所要求的。

这就像两者之间的区别:

echo *

echo ./*

顺便说一句,你也可以用其他表达式来做到这一点:

echo *.*(epub|mobi|chm|rtf|lit|djvu)

shell 正在为您完成所有的扩展。这真的与for 语句本身无关。

【讨论】:

  • +1 啊,是的,可爱的extglob。我忘了它有这个表情。
  • @David W.:感谢您的解释。顺便说一句echo *.*(epub|mobi|chm|rtf|lit|djvu) 给出错误:-bash: syntax error near unexpected token '('。我不知道为什么。
  • 你设置了shopt -s extglob?出于某种原因,Bash 不会自动进行扩展 blob。
【解决方案4】:

总而言之,最好的解决方案是使用(最直观和优雅):

shopt -s extglob
for f in *.*(epub|mobi|chm|rtf|lit|djvu)

或者,与引用线程中给出的原始解决方案保持一致(如上所述,这是错误的):

shopt -s nullglob
for f in "${arg}"*.{epub,mobi,chm,rtf,lit,djvu}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-27
    • 2014-05-16
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 2014-02-05
    • 1970-01-01
    相关资源
    最近更新 更多