Bash:空格和元字符例如*出现问题似乎不可能

问题描述

这是我的问题:我有一个名称列表,每行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
$

解决方法

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

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

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

unset IFS有效地恢复默认状态。默认状态是IFS设置为space-tab-newline,但是未设置的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仿真模式下使用。

,

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

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