如何在Perl中捕获完整的命令行?

如何在Perl中捕获完整的命令行?,perl,command-line,Perl,Command Line,假设电话是 /usr/local/bin/perl verify.pl 1 3 de# > result.log 在verify.pl内部,我想捕获上面的整个调用,并将其附加到日志文件中以便于跟踪 如何按原样捕获整个调用?$0有脚本名,@ARGV有参数,因此整个命令行是: $commandline=$0。" ". (加入“”@ARGV) 或者,更优雅地说(谢谢): $commandline=join”“,$0,@ARGV 但是,我不知道如何捕获重定向(>result.log)请参阅有一种

假设电话是

/usr/local/bin/perl verify.pl 1 3 de# > result.log
verify.pl
内部,我想捕获上面的整个调用,并将其附加到日志文件中以便于跟踪


如何按原样捕获整个调用?

$0
有脚本名,
@ARGV
有参数,因此整个命令行是:

$commandline=$0。" ". (加入“”@ARGV)

或者,更优雅地说(谢谢):

$commandline=join”“,$0,@ARGV

但是,我不知道如何捕获重定向(
>result.log

请参阅有一种方法(至少在unix系统上)可以获取整个命令行:

my $cmdline = `ps -o args -C perl | grep verify.pl`;
print $cmdline, "\n";
e:使用PID的清洁方式(由Nathan Fellman提供):

$commandline=join”“,$0,@ARGV
不处理命令行中有引号的情况,例如
/xxx.pl--love“dad and mom”

快速解决方案:

my $script_command = $0;
foreach (@ARGV) {
$script_command .= /\s/ ?   " \'" . $_ . "\'"
                :           " "   . $_;
}
尝试将以下代码保存为
xxx.pl
并运行
/xxx.pl--love“爸爸妈妈”


这里实际上是相同的Linux版本(当然,在shell干预之后):

纯perl

BEGIN {
    my @cmd = ( );
    if (open(my $h, "<:raw", "/proc/$$/cmdline")) {
        # precisely, buffer size must be at least `getconf ARG_MAX`
        read($h, my $buf, 1048576); close($h);
        @cmd = split(/\0/s, $buf);
    };
    print join("\n\t", @cmd), "\n";
};

处理更多特殊情况(且不添加自己的名称)和使用GetOpt处理行的示例:

#!perl
use strict;
use warnings;
use Getopt::Long qw(GetOptionsFromString);
use feature qw ( say );

# Note: $0 is own name

my $sScriptCommandLine;
my @asArgs = ();

my $iRet;

my $sFile = '';
my $iN = -1;
my $iVerbose = 0;

# ============================================================================
my %OptionsHash = (
    "f=s"               => \$sFile,
    "n=i"               => \$iN,
    "v:+"               => \$iVerbose);



$sScriptCommandLine = join( ' ', @ARGV );  # useless for argument with spaces
say 'A: ' . $sScriptCommandLine;

$sScriptCommandLine = '"' . join( '" "', @ARGV ) . '"'; # all arguments in "", but not suitable for arguments with '"' (given as '\"')
say 'B: ' . $sScriptCommandLine;

$sScriptCommandLine = '';
foreach (@ARGV) {
  $sScriptCommandLine .= ' ' if ($sScriptCommandLine);
  $_ =~ s/\\/\\\\/g; # needed for GetOptionsFromString
  $_ =~ s/\"/\\\"/g;
  if (/\s/) {
    $sScriptCommandLine .= '"'.$_.'"';
  }
  else {
    $sScriptCommandLine .= $_;
  }
}
say 'C: ' . $sScriptCommandLine;


my ($iRet,$paArgs);
($iRet,$paArgs) = GetOptionsFromString($sScriptCommandLine,%OptionsHash);
# remaining parameters in $$paArgs[0] etc. 
if (!$iRet) {
  # error message already printed from GetOptionsFromString
  print "Invalid parameter(s) in: \"$sScriptCommandLine\"\n";
}

say 'D: ' . '<<' . join( '>>  <<', @{$paArgs} ) . '>>';
say 'f=s: "'.$sFile.'"'; 
say 'n=i: '.$iN; 
say 'v:+: '.$iVerbose; 

# eof
#!perl
严格使用;
使用警告;
使用Getopt::Long qw(GetOptionsFromString);
使用特征qw(例如);
#注意:$0是自己的名字
我的$sScriptCommandLine;
我的@asArgs=();
我的$iRet;
我的$sFile='';
我的$iN=-1;
我的$iVerbose=0;
# ============================================================================
我的%OptionsHash=(
“f=s”=>\$s文件,
“n=i”=>\$iN,
“v:+”=>\$iVerbose);
$sScriptCommandLine=join(“”,@ARGV)#不适用于与空格进行辩论
说“A:”$sScriptCommandLine;
$sScriptCommandLine=''''''''。连接(''''''''',@ARGV)。''''.#“”中的所有参数,但不适用于带“”的参数(以“\”形式给出)
说“B:”$sScriptCommandLine;
$sScriptCommandLine='';
foreach(@ARGV){
$sScriptCommandLine.=''如果($sScriptCommandLine);
GetOptionsFromString需要$\\=~s/\\\/\\\\\\\\/g;#
$\\=~s/\“/\\\”/g;
如果(/\s/){
$sScriptCommandLine.=''.$'.'''.'''.'''.'''.';
}
否则{
$sScriptCommandLine.=$\ux;
}
}
说“C:”$sScriptCommandLine;
我的($iRet,$paArgs);
($iRet,$paArgs)=GetOptionsFromString($sScriptCommandLine,%OptionsHash);
#$$paArgs[0]等中的剩余参数。
如果(!$iRet){
#已从GetOptionsFromString打印错误消息
打印“$sScriptCommandLine\”中的“无效参数”\n;
}
说“D:”;
说“f=s:”.$sFile.”;
说‘n=i:’。$iN;
说“v:+:”。$iVerbose;
#eof

我认为您不能,因为shell只是
dup
将STDOUT的文件描述符指向该日志文件。据我所知,Perl无法知道是否发生了这种情况。(您可以通过使用
-t
来检测是否发生了输入重定向。)FWIW,
join
接受多个参数:
join(“,$0,@ARGV)
。可以在运行时使用
$^X
$Config{perlpath}
访问perl可执行文件的路径(如果您已加载
Config
模块)。
$Config{perlpath}
在某些情况下更可取,因为它是一个只读变量。无法捕获重定向。它由shell管理,即shell(bash、sh、cmd——无论什么)将打开文件然后在不使用
>
的情况下运行该命令。但是,可以猜测某个文件句柄是终端、文件还是管道。请注意,
@ARGV
会被
GetOptions()
使用,因此您需要在之前执行此操作。这很好,除非您运行了多个perl实例。您可以使用
getpid>过滤掉它()
@Nathan Fellman:是的,从流程中获得正确的答案可能会有一些问题,但我认为OP可以自己决定如何正确过滤特定流程。通过PID或某种grep模式,或者……我正在考虑使用类似于打印qx/ps-o args$$$/;
的方法,为什么不编辑您的答案并获得更多的投票呢?:-)这似乎无法维护命令行上使用的shell引用,这可能是一个问题,取决于原始命令行的用途。这太神奇了。这正是我要寻找的。你能解释一下这是如何工作的吗?不确定你在这里做什么
BEGIN {
    my @cmd = ( );
    if (open(my $h, "<:raw", "/proc/$$/cmdline")) {
        # precisely, buffer size must be at least `getconf ARG_MAX`
        read($h, my $buf, 1048576); close($h);
        @cmd = split(/\0/s, $buf);
    };
    print join("\n\t", @cmd), "\n";
};
BEGIN {
    use File::Slurp;
    my @cmd = split(/\0/s, File::Slurp::read_file("/proc/$$/cmdline", {binmode => ":raw"}));
    print join("\n\t", @cmd), "\n";
};
#!perl
use strict;
use warnings;
use Getopt::Long qw(GetOptionsFromString);
use feature qw ( say );

# Note: $0 is own name

my $sScriptCommandLine;
my @asArgs = ();

my $iRet;

my $sFile = '';
my $iN = -1;
my $iVerbose = 0;

# ============================================================================
my %OptionsHash = (
    "f=s"               => \$sFile,
    "n=i"               => \$iN,
    "v:+"               => \$iVerbose);



$sScriptCommandLine = join( ' ', @ARGV );  # useless for argument with spaces
say 'A: ' . $sScriptCommandLine;

$sScriptCommandLine = '"' . join( '" "', @ARGV ) . '"'; # all arguments in "", but not suitable for arguments with '"' (given as '\"')
say 'B: ' . $sScriptCommandLine;

$sScriptCommandLine = '';
foreach (@ARGV) {
  $sScriptCommandLine .= ' ' if ($sScriptCommandLine);
  $_ =~ s/\\/\\\\/g; # needed for GetOptionsFromString
  $_ =~ s/\"/\\\"/g;
  if (/\s/) {
    $sScriptCommandLine .= '"'.$_.'"';
  }
  else {
    $sScriptCommandLine .= $_;
  }
}
say 'C: ' . $sScriptCommandLine;


my ($iRet,$paArgs);
($iRet,$paArgs) = GetOptionsFromString($sScriptCommandLine,%OptionsHash);
# remaining parameters in $$paArgs[0] etc. 
if (!$iRet) {
  # error message already printed from GetOptionsFromString
  print "Invalid parameter(s) in: \"$sScriptCommandLine\"\n";
}

say 'D: ' . '<<' . join( '>>  <<', @{$paArgs} ) . '>>';
say 'f=s: "'.$sFile.'"'; 
say 'n=i: '.$iN; 
say 'v:+: '.$iVerbose; 

# eof