在C或C ++中控制shell命令行通配符扩展

时间:2010-04-11 15:07:27

标签: c++ shell command-line unix wildcard

我正在用C ++编写一个程序foo。它通常在命令行上调用,如下所示:

foo *.txt

我的main()以正常方式接收参数。在许多系统上,argv[1]实际上是*.txt,我必须调用系统例程来进行通配符扩展。但是,在Unix系统上,shell会在调用我的程序之前扩展通配符,并且所有匹配的文件名都将位于argv中。

假设我想在foo中添加一个开关,导致它递归到子目录。

foo -a *.txt

将处理当前目录及其所有子目录中的所有文本文件。

我看不出这是怎么做的,因为当我的程序有机会看到-a时,shell已经完成了扩展并且用户的*.txt输入丢失了。然而,有一些常见的Unix程序以这种方式工作。他们是怎么做到的?

在Unix领域,如何控制通配符扩展?

(通过子目录递归只是一个例子。理想情况下,我试图理解控制通配符扩展的一般解决方案。)

4 个答案:

答案 0 :(得分:6)

您的程序对shell的命令行扩展没有影响。在完成所有扩展后确定将调用哪个程序,因此以编程方式更改有关扩展的任何内容已经太晚了。

另一方面,调用程序的用户可以创建他喜欢的任何命令行。 shell允许您轻松防止通配符扩展,通常将参数放在单引号中:

program -a '*.txt'

如果您的程序被调用,则会收到两个参数-a*.txt

在Unix上,如果不需要,您应该将其留给用户手动阻止通配符扩展。

答案 1 :(得分:3)

正如其他答案所说,shell执行通配符扩展 - 并且通过将参数括在引号中来阻止它。

请注意,选项-R-r通常用于表示递归 - 请参阅cpls等示例。

假设您正确组织了一些事情,以便将通配符作为通配符传递给您的程序,并且您想要进行递归,那么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()将其扩展为名称列表:

    1. 生成文件
    2. mapmain.pl
    3. minimac.group
    4. minimac.passwd
    5. minimac_13.terminal
    6. mkmax.sql.bz2
  • 然后shell安排运行带有9个参数的“/bin/ls”(程序名,选项-R1,加上7个文件名和终止空指针)。

  • ls命令会记录选项(递归和单列输出),并开始工作。
    • 前6个名字(实际上是)是简单的文件,所以没有任何递归的。
    • 姓氏是一个目录,因此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 *
*