从 Perl 调用系统命令

发布于 2024-10-06 22:47:23 字数 2375 浏览 0 评论 0原文

在旧版本的代码中,我们从 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

月隐月明月朦胧 2024-10-13 22:47:23

一种方法是使用 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.

段念尘 2024-10-13 22:47:23

我将使用 IPC::Run3 来实现此目的。这很像 open '-|' 方法,但也允许您重定向 STDERR。

注意:$lldap_output 是从 ldapsearch 读取的管道。磁盘上没有创建任何文件。

如果您想要磁盘上的文件,您可以像这样使用 IPC::Run3:

use IPC::Run3;

my ($lcmd, @cmd_args) = ... # same as approach (2) above
my $lworkfile         = ... # same as approach (1) above

run3 [ $lcmd, @cmd_args ], undef, $lworkfile, $lworkfile;

这类似于方法 (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 from ldapsearch. There's no file being created on disk.

If you want a file on disk, you could use IPC::Run3 like this:

use IPC::Run3;

my ($lcmd, @cmd_args) = ... # same as approach (2) above
my $lworkfile         = ... # same as approach (1) above

run3 [ $lcmd, @cmd_args ], undef, $lworkfile, $lworkfile;

This is like approach (1), but using -b instead of $ENV{LDAP_BASEDN}.

坚持沉默 2024-10-13 22:47:23

感谢格雷格休吉尔的回答。我在下面发布我的代码,以防它对其他想要使用 open3 函数的人有所帮助。

use File::Copy;
use IPC::Open3;

# 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";
my $lldap_output;

# First arg is undef as I don't need to pass any extra input to the 
# process after it starts running.
my $pid = open3(undef, $lldap_output, $lldap_output, $lcmd, @cmd_args);

# Wait for the process to complete and then inspect the return code.
waitpid($pid, 0);

my $ldap_retcode = $? >> 8;

if ($ldap_retcode != 0)
{
  # Handle error
}

# Copy the output to $lworkfile so I can refer to it later if needed       
copy($lldap_output, $lworkfile);

while (my $lline = <$lldap_output>)
{
  # I can parse the contents of my file fine
}

$lldap_output->close;

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.

use File::Copy;
use IPC::Open3;

# 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";
my $lldap_output;

# First arg is undef as I don't need to pass any extra input to the 
# process after it starts running.
my $pid = open3(undef, $lldap_output, $lldap_output, $lcmd, @cmd_args);

# Wait for the process to complete and then inspect the return code.
waitpid($pid, 0);

my $ldap_retcode = $? >> 8;

if ($ldap_retcode != 0)
{
  # Handle error
}

# Copy the output to $lworkfile so I can refer to it later if needed       
copy($lldap_output, $lworkfile);

while (my $lline = <$lldap_output>)
{
  # I can parse the contents of my file fine
}

$lldap_output->close;
云裳 2024-10-13 22:47:23

请参阅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.

血之狂魔 2024-10-13 22:47:23

这是一种使用普通 ol'“open”从具有多个参数的外部程序读取 STDOUT 和 STDERR 的巧妙方法:

my @command_with_arguments = (YOUR_PROGRAM, ARG1, ARG2, ARG3);
foreach(@command_with_arguments){s/'/'"'"'/g;}
foreach(@command_with_arguments){s/(.+)/'$1'/;}
my $run_command = join (' ', @command_with_arguments) . " 2>&1 |";
open my $program_output, $run_command;

现在只需读取 $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":

my @command_with_arguments = (YOUR_PROGRAM, ARG1, ARG2, ARG3);
foreach(@command_with_arguments){s/'/'"'"'/g;}
foreach(@command_with_arguments){s/(.+)/'$1'/;}
my $run_command = join (' ', @command_with_arguments) . " 2>&1 |";
open my $program_output, $run_command;

Now just read $program_output to get both STDOUT and STDERR.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文