65.9K
CodeProject 正在变化。 阅读更多。
Home

Kamal Shankar 快速工具 - 递归执行器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.15/5 (7投票s)

2004年4月22日

GPL3

8分钟阅读

viewsIcon

47521

downloadIcon

1103

你是否曾经想要一个程序,能够从用户选择的目录中递归地运行另一个程序,并提供完整的标准输入/输出重定向、程序超时设置等功能?更重要的是,你可以轻松修改源代码以满足自己的需求。Kamal Shankar 为你带来了这样一个快速工具。

RecursiveExecuter main dialog

引言

我是一个工具收藏家——四处搜寻别人写过的工具,以便为我自己的微小需求加以利用……如果工具附带源代码,那更是锦上添花,但这种情况并不常见。

最近,我发现了一些仅有二进制文件的工具,它们只在被调用的目录中运行。我真的很希望它们能够深入到子目录中进行操作。

例如,我有一个很棒的工具,可以计算你传递给它的文件在指定目录中的 MD5 摘要,但它没有允许它递归操作的选项。

我还有许多其他工具,它们都能很好地完成自己的工作,但缺少这个功能。

虽然重写这些工具并不难,但我更喜欢偷懒,于是我想,为什么不写一个快速工具,从我指定的起始目录递归执行这些程序呢?这肯定比重写每一个我想添加此功能的工具要快得多!

经过一小时的快速开发,它就诞生了——RecursiveExecuter

RecursiveExecuter 的功能列表

  • 重定向控制台程序输出的选项。
  • 允许用户选择程序将在哪些(子)目录中运行的选项。
  • 允许你设置要递归执行的程序的超时时间。(默认情况下是无限)。
  • 允许你指定目录通配符,以缩小匹配范围。
  • 你可以指定程序参数,并能使用将被操作系统目录(当前匹配到的目录)替换的令牌

如何使用 RecursiveExecuter

虽然对话框上的选项应该不言自明,但我还是会提及一些细节,这些细节可能会帮助你更好地使用这个小工具。

注意

  1. 在下面的注释中,“子进程”是指你选择运行的程序。
  2. 你会遇到一个新的令牌“<>”,这是我选择的一个特殊令牌。你可以在任何你想让RecursiveExecuter放置它要递归进入的操作目录的地方使用这个令牌。

    例如,如果你希望当RecursiveExecuter深入到X:\ThisDir时,你的选定程序的参数是"-l -d:X:\ThisDir",当它深入到X:\ThisDir\SubDir时,参数是"-l -d:X:\ThisDir\SubDir",那么你在RecursiveExecuter的相应字段中输入以下内容:"-l -d:<>"

    RecursiveExecuter在运行时所做的就是简单地将“<>”替换为它刚刚找到的操作目录!

    我为什么要选择这个令牌? 因为在 Windows 中,它是一个非法的目录名——任何目录都不能包含这两个字符中的任何一个!

  3. 操作目录”是指RecursiveExecuter在递归过程中当前找到的目录。这与“当前目录”的含义不同,后者是指传递给子进程的目录变量(由“Current Dir Value”字段指定)。

    但是,如果你将“Current Dir Value”字段的值保留为默认的“<>”,那么两者将具有相同的值。

    例如,如果RecursiveExecuter刚刚遇到了目录X:\RootDir\SubDir\AnotherDir,那么操作目录就是X:\RootDir\SubDir\AnotherDir

    RecursiveExecuter将令牌“<>”解释为操作目录的含义。

以下是对话框字段的详细说明

  • 程序选项:

    • Redirect STDIO:此选项允许你将控制台程序的输出重定向/捕获到一个你指定的文本文件中。默认情况下它是未勾选的,当你勾选它时,将弹出一个文件对话框,你可以在其中选择输出文件名,输出将被重定向到该文件。

      注意:勾选此选项时,子进程窗口也将隐藏,也就是说,你将看不到通常会看到的控制台窗口。

    • Confirm new entry:由于此程序会递归进入目录,你可能希望过滤掉一些不希望程序对其进行操作的目录。

      勾选此框后,每当RecursiveExecuter切换到新目录时,它都会询问你是否允许子进程在该目录中运行。

      如果你想跳过在该目录中进行操作,只需在对话框中选择“否”即可。

    • Timeout (ms):默认情况下,它是-1,表示在运行的进程上处于无限等待状态(这意味着RecursiveExecuter将耐心等待子进程完成)。建议将其保持原样。

      但是,你可能有自己的理由来调整RecursiveExecuter等待子进程完成的时间。如果子进程在字段中指定的时间量后仍然处于活动状态,RecursiveExecuter将简单地将其终止。

    • Directory wildcard:如果你想微调RecursiveExecuter将要进入的目录列表,此选项会很有用。

      默认情况下,它是空的,这意味着RecursiveExecuter将进入“Select the starting directory”字段中指定的(子)目录及其所有子目录。

      除了以上之外,它将被解释为 Windows Find 的方式。例如,值为 '*' 将使RecursiveExecuter在指定目录的子目录中运行,或者值为 'r*' 将匹配所有以 'r' 开头的(子)目录。

    • Current Dir value:子进程是使用CreateProcess()调用创建的。CreateProcess()的一个参数是LPCTSTR lpCurrentDirectory,它指定子进程的当前驱动器和目录。

      虽然此值可以为NULL,但许多程序在传递有意义的值(通常是当前工作目录)时运行得更好。

      事实上,大多数控制台程序都通过此参数传递的目录执行所需的操作。

      你可以将此字段视为创建程序快捷方式时填写的“Working Directory”或“Start In”字段。

      如果不确定,应将其保留为默认值“<>”。


  • 执行选项
    • Select the program you want to execute : 选择你要递归执行的程序的路径。直接输入(必须是完整限定文件名),或使用旁边的Browse按钮。

      这个程序将被创建为子进程。

    • Select the starting directory : 选择你希望RecursiveExecuter开始递归的起始目录。
    • Executable arguments : 输入你希望传递给子进程的参数(命令行选项)。

      同样,如果你想将操作目录传递给命令行,只需将“<>”令牌(不带引号)放在相应位置即可!


关注点

CreateProcess() 的行为

虽然 MSDN 的“INFO: Understanding CreateProcess and Command-line Arguments”的“Case 3”中对此有详细说明,但我仍将在此简要提及。

让我们更详细地查看CreateProcess()的原型。

BOOL CreateProcess(
  LPCTSTR lpApplicationName,                 // name of executable module

  LPTSTR lpCommandLine,                      // command line string

....);

要创建控制台程序,我们可以将lpApplicationName设置为NULL,但lpCommandLine必须至少包含(完整限定的)进程文件名。额外的命令行参数可能像往常一样跟在后面。

但是,如果我们把完整限定的程序名传递给lpApplicationName,而把命令行的其余部分传递给lpCommandLine,那么控制台进程可以被创建,但main()的参数将不匹配。

为了说明我的意思,假设我们有一个控制台程序"prog.exe"。它接受许多命令行开关,其中一个开关是"-h -m simple",它会打印出一个简单的帮助页面。

如果我们使用CreateProcess()像这样创建进程:

....
char szArgs[MAX_ARGS];
lstrcpy(szArgs,"-h -m simple");

CreateProcess("X:\\prog.exe",szArgs,...);

那么进程将被创建,但命令行参数将具有以下值:

argv[0] == "-h"
argv[1] == "-m"
argv[2] == "simple"

但当然,ANSI 规范要求值看起来像:

argv[0] == "X:\\prog.exe"
argv[1] == "-h"
argv[2] == "-m"
argv[3] == "simple"

我们作为优秀的程序员,一直遵循 ANSI 规范进行编码。所以,任何遵循此 ANSI 规范的控制台程序都会出现问题。

现在,RecursiveExecuter在默认启动程序时会考虑这一点,但如果你有不遵循 ANSI 规范,而是依赖CreateProcess这种行为的程序,你可以手动删除自动添加到RecursiveExecuter的“Executable arguments”字段中的程序名,一切都会正常。

BUG

有时,在运行启用了Redirect STDIO的控制台应用程序时,可能会出现“挂起”RecursiveExecuter的情况。但是,在终止子进程后,RecursiveExecuter将恢复任务。

令人惊讶的是,字段设置的组合完全相同,只是Redirect STDIO(现在已禁用)除外,不会产生任何问题。

修复这个问题需要一些时间,但我认为这与管道有关。如果你们中的任何人能发现问题(并修复它?),请告诉我。

总结

这个工具对我来说运行良好,我也没有见过它运行得不好。如果它对你有用,并且你喜欢它,请评分,然后幸福地生活,并记住是我写的。

如果你有 bug 报告、功能请求或任何其他问题,请在此消息板上发帖,或给我打个电话(发邮件给我)。

如果这个程序在你的电脑上制造了一个黑洞或让你隐形了,请记住,它是被一个同时也在观看Whose Line Is It Anyway的人编写的。你已被警告。去责怪 Drew 吧 ;)

历史

2004/04/20 - 初始发布。

© . All rights reserved.