【问题标题】:In Unix (bash), how to append a wildcard to a variable?在 Unix (bash) 中,如何将通配符附加到变量?
【发布时间】:2015-12-14 10:10:58
【问题描述】:

我的工作需要我循环浏览 BigDirectory 中的每个目录,然后遍历每个目录中的每个文件。比如:

for d in BigDirectory/*/
do
 FILES=$d*
 for f in $FILES
 do
  echo "File $f in directory $d"
 done
done

我的问题是关于 FILES=$d*。当我在命令行中执行此命令时,它会像预期的那样工作 - 也就是说,对于单次迭代, echo $FILES 显示

BigDirectory/directory1/file1
BigDirectory/directory1/file2
BigDirectory/directory1/file3

但是当我将此脚本作为我的集群上的作业提交时,它不起作用 - 如果我要回显 $FILES,我会看到它一直在寻找具有文字文件名“BigDirectory/directory1/*”的东西- 就像它没有注册星号一样。

有人知道会发生什么吗?

编辑 对我有用的是将 shopt -s nullglob 放在每个循环之前,并在开头添加一行“cd 为集群定义在哪里查找文件(因此它不会扩展 *)。感谢您的帮助全部

【问题讨论】:

  • ...将整个内容放入 FILES 变量中只是有损/产生错误,因为这意味着您不能仅扩展第一部分而不扩展后者——在扩展 $FILES 时,要么引用它(最后的 * 不起作用)要么不引用(目录名称中的任何空格或 glob 都会导致该名称可能评估为某些东西除了自己)。现在,您可以做的是将 glob 的结果收集到一个变量中:files=( "$d"/* ); for file in "${files[@]}"; do echo "found file $file in $d"; done.
  • 如果您想要处理子树 BigDirectory 中或下的每个文件,您可能希望使用find BigDirectory -type f -exec ...

标签: bash unix


【解决方案1】:

如果通配符不匹配任何文件,则结果将是文字字符串本身。为了防止在设置nullglob 选项的情况下运行,并在for 循环中使用通配符。

shopt -s nullglob
 ...
for f in "$d"*
do
   ...
done

【讨论】:

  • 我认为 OP 实际上可能想要"$d"/*,而不是"$d"*。
  • @CharlesDuffy:我已经验证了外部循环将以/ 结束。
  • 啊。是的,对于给出的 OP 数据,这将起作用……尽管我认为依赖它并不是特别好的形式。 // 允许通过 POSIX 调用 OS 定义的行为,是的,但 IIRC 仅当出现在路径的最开始时;否则,这是一种无害的措施,并减少了对预期输入数据的依赖量。
  • 由于某种原因,当我将它包含在 BigDirectory/*/ 循环中的 for d 之外时,我的脚本似乎根本没有运行......作业文件以 .当我将 shopt -s 放在 "$d"* 循环中的 for f 之前, echo "$d" 输出 BigDirectory/*/ 而不是 BigDirectory/directory1/ 。
  • @KM:这意味着它没有匹配任何东西。
【解决方案2】:

当 glob(通配符 * 的正确名称)与任何文件都不匹配时,它会保留下来,并且会像文件一样打印出来(实际上不是)。
使用 bash 选项 nullglob 可以更改这种扩展 glob 的方式。如果设置,则 glob 会消失,并且不会用作文件。

另外,为每个目录使用$d/* 是个好主意,您使用的表单很容易失败。如果它结束为两个//,则没有问题,它们将被解析为使用一个/。


亲吻

但是没有必要做所有这些工作,这个简单的命令:

set -- BigDirectory/**;

将大列表中的所有文件(甚至在目录和子目录中)设置为位置参数。那么,一个循环就足够了。

所以,没有必要重新扫描每个目录,一切都可以用**(在bash中)完成:

shopt -s globstar nullglob
set -- BigDirectory/**;
for d;
do
    echo "$d";
done

如果您想将选项恢复到原来的状态,请将所有代码包含在 (...) 中。

【讨论】:

  • ** 递归通配符仅适用于 Bash v4(和 zsh),我相信。
  • 此外,大多数明智的 Unix 命令都接受通配符; printf '%s\n' BigDirectory/**(假设你没有点击ARG_MAX)。
  • 命令是否接受通配符无关;对于 all 命令,shell 会扩展未加引号的模式。
  • @tripleee 是的,bash 高于 4.0,是的,循环可以替换为 printf。努力做到:为海报“一步一步”。
  • 例如,printf 不使用通配符。它只需要一个参数列表,这些参数可能由 shell from 通配符创建。如果当前目录的内容正好包含两个文件,foo 和 bar,printf 将无法区分 printf "%s" * 和 printf "%s" foo bar。真正可以使用通配符的命令示例是find;它是 -name primary 采用由 find 本身扩展的模式。
猜你喜欢
  • 1970-01-01
  • 2021-02-15
  • 2013-09-04
  • 2015-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
  • 1970-01-01
相关资源
最近更新 更多