编辑:只是为了澄清,我想知道如何实现内容管道到exec
进程,放在一边 Perl是否提供更好的方法来实现不涉及此技术的相同最终结果的问题。
使用玩具 zsh
脚本示例更容易描述我想要做的事情:
#!/usr/bin/env zsh
# -----------------------------------------------------------------------------
# handle command-line arguments if any
# nb: ${(%):-%x} is zsh-speak for "yours truly"
# (( $# % 2 )) && (print -rl -- "$@"; [[ -t 0 ]] || cat) | exec ${(%):-%x}
(( $# > 0 )) && ([[ -t 0 ]] || cat; print -rl -- "$@") | exec ${(%):-%x}
# -----------------------------------------------------------------------------
# standard operation below this line
nl
上面的脚本几乎是nl
("数字行")实用程序的传递包装器,不同的是,如果存在命令行参数,它会将它们附加到到它的标准。例如:
$ seq 3 | /tmp/nlwrapper.sh
1 1
2 2
3 3
$ seq 3 | /tmp/nlwrapper.sh foo bar baz
1 1
2 2
3 3
4 foo
5 bar
6 baz
请注意
命令行参数可以很容易地 prepended 到stdin(事实上,如果取消注释脚本的第7行,生成的脚本将会预先添加或追加stdin的命令行参数取决于它们的数字是奇数还是偶数); 我对两种功能感兴趣。
该脚本由两个完全独立的部分组成:一个处理命令行参数的前导码(如果有的话)和正文(在这个玩具示例中由一行组成)它负责脚本的主要功能(编号行); 这是必不可少的设计功能。
进一步阐述这两点:"身体" section通过命令行参数了解有关业务的 nothing 。实现命令行参数处理的代码几乎可以预先设置" as-is"到任何处理stdin的zsh
脚本。此外,对命令行参数的处理方式(prepend vs append等)的更改会使注释# standard operation below this line
下面的所有内容保持不变。这两个部分真正彼此独立。
Perl脚本中上述序言的等价物是什么?
我知道上面脚本的Perl等价物将具有一般形式
#!/usr/bin/env perl
use strict;
use English;
# -----------------------------------------------------------------------------
# handle command-line arguments if any
if ( @ARGV > 0 ) {
# (mumble, mumble) ... -t STDIN ... exec $PROGRAM_NAME;
}
# -----------------------------------------------------------------------------
# standard operation below this line
printf "%6d\t$_", $., $_ while <>;
我的问题是实现这一点:
([[ -t 0 ]] || cat; print -rl -- "$@") |
我知道
[[ -t 0 ]]
测试是-t STDIN
; cat
部分可以使用print while <>
实现;和print -rl -- "$@"
位可以使用CORE::say for @ARGV
我不知道如何将这些元素放在一起以获得所需的功能。
答案 0 :(得分:3)
除非Perl脚本需要像shell脚本一样神秘,否则我会做这样的事情:
#!/usr/bin/env perl
use strict;
use warnings;
use autouse Carp => qw( croak );
use IO::Interactive qw( is_interactive );
run( \*STDIN, is_interactive() ? [] : (\@ARGV, [qw(ya ba da ba doo)]) );
sub run {
my $appender = mk_appender(@_);
printf "%6d\t%s", @$_ while $_ = $appender->();
}
sub mk_appender {
my $fh = shift;
my $line_number = 0;
my $i = 0;
my @readers = (
sub { scalar <$fh> },
(
map { my $argv = $_; sub {
($i < @$argv) ? $argv->[$i++] . "\n" : ();
}} @_
),
);
return sub {
@readers or return;
while ( @readers ) {
my $line = $readers[0]->();
return [++$line_number, $line] if defined $line;
shift @readers;
$i = 0;
}
return;
};
}
输出:
$ seq 3 | perl t.pl foo bar baz 1 1 2 2 3 3 4 foo 5 bar 6 baz 7 ya 8 ba 9 da 10 ba 11 doo
使用Perl提供的功能而不是尝试复制shell脚本的一些优点包括以下事实:您可以避免产生其他进程,读取整个标准输入两次等。
我还写了关于在How to sum data from multiple files in Perl?
中同时阅读多个文件的文章如果您打算两次阅读相同的输入,请参阅pipe和Bidirectional communication with yourself。
答案 1 :(得分:1)
使用cat
和一些常见的shell功能可能会更容易。 (bash
此处使用。)
cat <( seq 3 ) <( printf 'foo\nbar\nbaz\n' ) | prog
解决方案:
use POSIX qw( );
sub munge_stdin {
pipe(my $r, my $w) or die("pipe: $!");
$w->autoflush();
local $SIG{CHLD} = 'IGNORE';
defined( my $pid = fork() ) or die("fork: $!");
if (!$pid) {
eval {
close($r) or die("close pipe: $!");
while (<STDIN>) {
print($w $_) or die("print: $!");
}
for (@ARGV) {
print($w "$_\n") or die("print: $!");
}
POSIX::_exit(0);
};
warn($@);
POSIX::_exit(1);
}
close($w) or die("close pipe: $!");
open(STDIN, '<&', $r) or die("dup: $!");
@ARGV = ();
}
munge_stdin();
print while <>; # or: exec("cat") or die("exec: $!");
exec()
。