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