我正在用C ++编写一个程序foo。它通常在命令行上调用,如下所示:
foo *.txt
我的main()
以正常方式接收参数。在许多系统上,argv[1]
实际上是*.txt
,我必须调用系统例程来进行通配符扩展。但是,在Unix系统上,shell会在调用我的程序之前扩展通配符,并且所有匹配的文件名都将位于argv
中。
假设我想在foo中添加一个开关,导致它递归到子目录。
foo -a *.txt
将处理当前目录及其所有子目录中的所有文本文件。
我看不出这是怎么做的,因为当我的程序有机会看到-a
时,shell已经完成了扩展并且用户的*.txt
输入丢失了。然而,有一些常见的Unix程序以这种方式工作。他们是怎么做到的?
在Unix领域,如何控制通配符扩展?
(通过子目录递归只是一个例子。理想情况下,我试图理解控制通配符扩展的一般解决方案。)
答案 0 :(得分:6)
您的程序对shell的命令行扩展没有影响。在完成所有扩展后确定将调用哪个程序,因此以编程方式更改有关扩展的任何内容已经太晚了。
另一方面,调用程序的用户可以创建他喜欢的任何命令行。 shell允许您轻松防止通配符扩展,通常将参数放在单引号中:
program -a '*.txt'
如果您的程序被调用,则会收到两个参数-a
和*.txt
。
在Unix上,如果不需要,您应该将其留给用户手动阻止通配符扩展。
答案 1 :(得分:3)
正如其他答案所说,shell执行通配符扩展 - 并且通过将参数括在引号中来阻止它。
请注意,选项-R
和-r
通常用于表示递归 - 请参阅cp
,ls
等示例。
假设您正确组织了一些事情,以便将通配符作为通配符传递给您的程序,并且您想要进行递归,那么POSIX提供了一些例程来帮助:
还有ftw
,它与nftw
非常相似,但标记为“过时”,因此新代码不应使用它。
但我可以说没有单引号的ls -R * .txt并获得递归列表。这有什么作用?
要将问题调整到我计算机上方便的位置,请查看:
$ ls -F | grep '^m'
makefile
mapmain.pl
minimac.group
minimac.passwd
minimac_13.terminal
mkmax.sql.bz2
mte/
$ ls -R1 m*
makefile
mapmain.pl
minimac.group
minimac.passwd
minimac_13.terminal
mkmax.sql.bz2
mte:
multithread.ec
multithread.ec.original
multithread2.ec
$
所以,我有一个包含三个文件的子目录'mte'。我有六个名字以'm'开头的文件。
当我输入'ls -R1 m *'时,shell会记下元字符'*'并使用其等效的glob()
或wordexp()
将其扩展为名称列表:
然后shell安排运行带有9个参数的“/bin/ls
”(程序名,选项-R1
,加上7个文件名和终止空指针)。
ls
命令会记录选项(递归和单列输出),并开始工作。
ls
会打印其名称及其内容,并调用其等效的nftw()
来完成工作。ls
首先处理非目录名,然后按字母顺序处理目录名(默认情况下),并对每个目录进行深度优先扫描。答案 2 :(得分:1)
foo -a '*.txt'
shell的部分工作(在Unix上)是扩展命令行通配符参数。你可以用引号来阻止它。
此外,在Unix系统上,“find”命令可以执行您想要的操作:
find . -name '*.txt'
将从当前目录中递归列出所有文件。
因此,你可以做到
foo `find . -name '*.txt'`
答案 3 :(得分:1)
我想指出另一种关闭通配符扩展的方法。您可以使用noglob
选项告诉shell停止扩展通配符。
使用bash使用set -o noglob
:
> touch a b c
> echo *
a b c
> set -o noglob
> echo *
*
使用csh,请使用set noglob
:
> echo *
a b c
> set noglob
> echo *
*