从 Perl 调用系统命令
在旧版本的代码中,我们从 Perl 调用进行 LDAP 搜索,如下所示:
# Pass the base DN in via the ldapsearch-specific environment variable
# (rather than as the "-b" paramater) to avoid problems of shell
# interpretation of special characters in the DN.
$ENV{LDAP_BASEDN} = $ldn;
$lcmd = "ldapsearch -x -T -1 -h $gLdapServer" .
<snip>
" > $lworkfile 2>&1";
system($lcmd);
if (($? != 0) || (! -e "$lworkfile"))
{
# Handle the error
}
上面的代码将导致成功的 LDAP 搜索,并且该搜索的输出将位于文件 $lworkfile.
不幸的是,我们最近在该服务器上重新配置了 openldap,以便在 /etc/openldap/ldap.conf 和 /etc/ldap.conf 中指定“BASE DC=”。该更改似乎意味着 ldapsearch 忽略 LDAP_BASEDN 环境变量,因此我的 ldapsearch 失败。
我尝试了几种不同的修复方法,但到目前为止没有成功:
(1) 我尝试重新使用 ldapsearch 的“-b”参数,但转义 shell 元字符。我开始编写转义代码:
my $ldn_escaped = $ldn;
$ldn_escaped =~ s/\/\\/g;
$ldn_escaped =~ s/`/\`/g;
$ldn_escaped =~ s/$/\$/g;
$ldn_escaped =~ s/"/\"/g;
这引发了一些 Perl 错误,因为我没有在 Perl 中正确转义这些正则表达式(行号与带反引号的正则表达式匹配)。
在 /tmp/mycommand 行 404 处的操作符预期位置找到反引号,位于行尾
同时我开始怀疑这种方法并寻找更好的方法。
(2) 然后我看到了一些 Stackoverflow 问题(此处 和 此处)建议了更好的解决方案。
这是代码:
print("Processing...");
# Pass the arguments to ldapsearch by invoking open() with an array.
# This ensures the shell does NOT interpret shell metacharacters.
my(@cmd_args) = ("-x", "-T", "-1", "-h", "$gLdapPool",
"-b", "$ldn",
<snip>
);
$lcmd = "ldapsearch";
open my $lldap_output, "-|", $lcmd, @cmd_args;
while (my $lline = <$lldap_output>)
{
# I can parse the contents of my file fine
}
$lldap_output->close;
我在方法(2)中遇到的两个问题是:
a)使用参数数组调用 open 或 system 不允许我传递 > 。 $lworkfile 2>&1
到命令,所以我无法阻止 ldapsearch 输出发送到屏幕,这使我的输出看起来很难看:
处理...ldap_bind:成功(0) 附加信息:成功
b) 我不知道如何选择传递给 open
的文件句柄的位置(即路径和文件名),即我不知道 $lldap_output
在哪里> 是。我可以移动/重命名它,或者检查它以找出它的位置(或者它实际上没有保存到磁盘)吗?
基于(2)的问题,这让我认为我应该回到方法(1),但我不太确定如何
In an older version of our code, we called out from Perl to do an LDAP search as follows:
# Pass the base DN in via the ldapsearch-specific environment variable
# (rather than as the "-b" paramater) to avoid problems of shell
# interpretation of special characters in the DN.
$ENV{LDAP_BASEDN} = $ldn;
$lcmd = "ldapsearch -x -T -1 -h $gLdapServer" .
<snip>
" > $lworkfile 2>&1";
system($lcmd);
if (($? != 0) || (! -e "$lworkfile"))
{
# Handle the error
}
The code above would result in a successful LDAP search, and the output of that search would be in the file $lworkfile
.
Unfortunately, we recently reconfigured openldap on this server so that a "BASE DC=" is specified in /etc/openldap/ldap.conf and /etc/ldap.conf. That change seems to mean ldapsearch ignores the LDAP_BASEDN environment variable, and so my ldapsearch fails.
I've tried a couple of different fixes but without success so far:
(1) I tried going back to using the "-b" argument to ldapsearch, but escaping the shell metacharacters. I started writing the escaping code:
my $ldn_escaped = $ldn;
$ldn_escaped =~ s/\/\\/g;
$ldn_escaped =~ s/`/\`/g;
$ldn_escaped =~ s/$/\$/g;
$ldn_escaped =~ s/"/\"/g;
That threw up some Perl errors because I haven't escaped those regexes properly in Perl (the line number matches the regex with the backticks in).
Backticks found where operator expected at /tmp/mycommand line 404, at end of line
At the same time I started to doubt this approach and looked for a better one.
(2) I then saw some Stackoverflow questions (here and here) that suggested a better solution.
Here's the code:
print("Processing...");
# Pass the arguments to ldapsearch by invoking open() with an array.
# This ensures the shell does NOT interpret shell metacharacters.
my(@cmd_args) = ("-x", "-T", "-1", "-h", "$gLdapPool",
"-b", "$ldn",
<snip>
);
$lcmd = "ldapsearch";
open my $lldap_output, "-|", $lcmd, @cmd_args;
while (my $lline = <$lldap_output>)
{
# I can parse the contents of my file fine
}
$lldap_output->close;
The two problems I am having with approach (2) are:
a) Calling open or system with an array of arguments does not let me pass > $lworkfile 2>&1
to the command, so I can't stop the ldapsearch output being sent to screen, which makes my output look ugly:
Processing...ldap_bind: Success (0) additional info: Success
b) I can't figure out how to choose which location (i.e. path and file name) to the file handle passed to open
, i.e. I don't know where $lldap_output
is. Can I move/rename it, or inspect it to find out where it is (or is it not actually saved to disk)?
Based on the problems with (2), this makes me think I should return back to approach (1), but I'm not quite sure how to
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
一种方法是使用
IPC::Open3
来启用用于处理外部程序的 stdout 和 stderr 流的 Perl 代码。One approach would be to use
IPC::Open3
to enable your Perl code to handle both the stdout and stderr streams of your external program.我将使用 IPC::Run3 来实现此目的。这很像
open '-|'
方法,但也允许您重定向 STDERR。注意:
$lldap_output
是从ldapsearch
读取的管道。磁盘上没有创建任何文件。如果您想要磁盘上的文件,您可以像这样使用 IPC::Run3:
这类似于方法 (1),但使用
-b
而不是$ENV{LDAP_BASEDN}
。I would use IPC::Run3 for this. This is much like the
open '-|'
approach, but allows you to redirect STDERR too.Note:
$lldap_output
is a pipe reading fromldapsearch
. There's no file being created on disk.If you want a file on disk, you could use IPC::Run3 like this:
This is like approach (1), but using
-b
instead of$ENV{LDAP_BASEDN}
.感谢格雷格休吉尔的回答。我在下面发布我的代码,以防它对其他想要使用 open3 函数的人有所帮助。
Thanks to Greg Hewgill for the answer. I'm posting my code below in case it helps anybody else wanting to use the open3 function.
请参阅open 的文档。您可以复制并重定向 STDERR,运行命令,然后恢复 STDERR。它比使用任何 IPC::(Open3、Run、Run3 等) 库都更详细,但如果您不能/不会安装额外的模块,或者不想使用 IPC,也可以不使用它们::打开3。
See the docs for open. You can duplicate and redirect STDERR, run your command, then restore STDERR. It's more verbose than using any of the IPC::(Open3, Run, Run3, etc.) libraries, but possible to do without them if you can't/won't install extra modules, or don't want to use IPC::Open3.
这是一种使用普通 ol'“open”从具有多个参数的外部程序读取 STDOUT 和 STDERR 的巧妙方法:
现在只需读取 $program_output 即可获取 STDOUT 和 STDERR。
Here's a hacky way to read both STDOUT and STDERR from an external program with multiple arguments using plain ol' "open":
Now just read $program_output to get both STDOUT and STDERR.