【问题标题】:Bash : problem with spaces and metacharacters like *. Seems impossibleBash:空格和元字符(如 *.似乎不可能
【发布时间】:2020-12-09 17:46:00
【问题描述】:

这是我的问题:我有一个名称列表,每行 1 个,可以匹配或不匹配系统上的某些文件。 我想使用元字符来匹配系统上的文件。

如果我有两个名称的列表:“a b c”和“re*”,并且在我的系统上有 3 个文件:reinc recsa reini

我想编写一些代码来处理 2 个参数“a b c”和“re*”,并获取文件列表每行一个。 我尝试的是错误的,结果我成功了

a
b
c
reinc
recsa
reini

a b c
reinc recsa reini

但似乎不可能得到

a b c
reinc
recsa
reini

你有什么想法吗?

我尝试过的示例:

$ ls
list  recsa  reinc  reini
$ cat list
a b c
re*
$ cat list | while read name; do echo $name; done
a b c
recsa reinc reini
$ cat list | while read name; do for file in $name; do echo "$file"; done; done
a
b
c
recsa
reinc
reini
$

【问题讨论】:

  • 很难理解您的输入和输出。我猜你的系统上有六个文件,“a b c reinc recsa reini”。如果他们被命名为别的东西怎么办?您要执行什么一般任务?
  • 你能澄清一下吗?你想写一个 bash 脚本吗?什么是“两个名字”?那会是那个脚本的论据吗?这两个参数与文件有何关系?如果两个参数中的任何一个是文件名的前缀,或者它只是第二个参数,是否应该打印文件名?您对前缀感兴趣,还是有不同的逻辑?
  • 你好,我在帖子里加了一个例子
  • 所以你有两行,每一行都是一个参数列表。您希望第一行逐字回显,第二行进行外壳扩展,每个结果都在新行上。文件中的行数会超过两行吗?第二行是否会包含多个模式?第一行会包含元字符,还是只包含文字字符串?
  • 这项任务需要很长时间才能描述,所以我将问题简化为对我来说有问题的问题。为了简化我这样做是因为我将通过 ssh 将结果传递给 tar 命令的 STDIN。我想扩展元字符,例如? * [],并处理带有空格的名称。文件可能存在也可能不存在,如果没有文件,结果有 re* 就可以了

标签: bash shell wildcard spaces


【解决方案1】:

如果我理解正确,您想对每个参数应用通配符扩展 (filename expansion),而不是 word splitting。 Bash 没有办法做到一个没有另一个:双引号(或其他类似双引号的上下文)之外的变量替换或命令替换执行两者,并且没有构造只执行一个。但两者都可以配置为无操作。

要有效禁用分词,请将IFS 设置为空字符串。

IFS=
while read -r line; do
  printf '%s\n' $line
done <list
unset IFS

unset IFS 有效地恢复了默认状态。默认状态是IFS 设置为空格制表符换行符,但未设置的IFS 对分词具有相同的效果。在 bash 中,您可以通过在函数中执行您需要的任何操作并声明 IFS 在此函数中为本地行为来保持本地行为:

act_on_list () {
  local IFS=
  while read -r line; do
    printf '%s\n' $line
  done <list
}
act_on_list

如果您想要相反的情况(在没有文件名扩展的情况下进行分词),您可以使用set -f 禁用文件名扩展。或者你可以使用read,它也可以进行分词,但你必须提前知道有多少个单词。

除了使用local,我的回答适用于所有非古董的 sh 变体,而不仅仅是 bash。 Zsh 有更好的方法,但标准方法也适用于 (k)sh 仿真模式。

【讨论】:

  • 好主意设置IFS=。您可以使用IFS=$'\n'; printf %s\\n $(&lt; list) 在没有循环的情况下应对。它需要更多的内存,但这应该不是问题。 Bash 的内置 printf 无论如何都不会被 ARG_MAX 怀疑。
  • 谢谢!我会试试看。我不知道 IFS 也会影响分词。你用几句话很好地描述了我的问题!事实上,我正在使用内置命令 echo 和 stdin/stdout 来处理 arg_max 限制,所以这个带有 printf 的解决方案看起来非常好。我不认为我可以只关闭 bash 功能,我想我还有很多东西要学,我应该阅读整个手册以提高效率
【解决方案2】:

使用compgen -G 来扩展通配符/glob,例如*?compgen 的输出是每行一个匹配项。如果没有匹配项(例如compgen -G nonexistent_file),则没有输出。如果我理解正确,那么在这种情况下你想打印nonexistent_file。您可以通过检查compgen 的存在状态来做到这一点。

while IFS= read -r pattern; do
  compgen -G "$pattern" || printf %s\\n "$pattern"
done < list

【讨论】:

  • 谢谢。有趣的!我也会详细尝试一下
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-22
  • 1970-01-01
相关资源
最近更新 更多