将 STDIN 保存到 Perl 中的文件?

Save STDIN to file in Perl?

我正在尝试使用 Perl 来:

  1. 等到至少有 1 个字节到达 STDIN
  2. 读取所有 STDIN 并将其保存为二进制文件(与 UTF-8 兼容)

我的尝试:

# read STDIN into a string/buffer and save it (maybe this only reads one line):
echo hello | perl -e 'open (fh, ">", "my_filename.txt"); print fh <STDIN>';

# or:
echo hello | perl -e 'open STDIN, ">", "my_filename.txt"'

# ideally be able to specify the filename:
echo hello | perl -e 'open STDIN, ">", $ARGV[0]' my_filename.txt

我不能使用 shell 来对 echo hello > my_filename.txt 执行此操作,因为 shell 的一个古老的 bug/feature 是同时打开所有文件句柄而不是顺序地基于一些依赖逻辑。所以我的计划是等到有字节在 STDIN 上等待,然后再打开目标文件。

不幸的是,我时间紧迫,没有时间重新学习 Perl 的语法。我认为我出错的地方是我试图在这里将 STDIN 称为新变量而不是实际的输入流。我看过其他各种缓冲解决方案,但不幸的是,它们在某些方面都不够充分(实际上不起作用,不是跨平台的,需要安装额外的可执行文件等)。

如果您直接执行重定向或通过遍历块以减少内存使用量,则可获得加分。知道如何从文件读取和写入 STDOUT 也会有所帮助。这也可能在 awk 之类的东西中实现,但请注意,我避免使用 sed 是因为 GNU sed 和 BSD sed 之间语法的另一个古老的错误选择经常阻止在两个平台上使用单个命令而不进行调整。所以 Perl 似乎是要走的路。

我不能确定,但​​我想你想做类似

的事情
cat foo | some_operation > foo

如果是这样,简单的解决方案是

cat foo | some_operation | sponge foo

但是你直接要求的可以实现如下:

perl -e'
   @ARGV or die("usage\n");
   my $qfn = shift;
   my $line = <>;
   open(STDOUT, ">", $qfn)
      or die("open $qfn: $!\n");
   print $line;
   print while <STDIN>;
' file.out

我们可以利用-p

perl -pe'
   BEGIN {
      @ARGV or die("usage\n");
      $qfn = shift(@ARGV);
   }
   if ($. == 1) {
      open(STDOUT, ">", $qfn)
         or die("open $qfn: $!\n");
   }
' file.out

我们可以投入 -s.

perl -spe'
   BEGIN { defined($o) or die("usage\n"); }
   if ($. == 1) {
      open(STDOUT, ">", $o)
         or die("open $o: $!\n");
   }
' -- -o=file.out

最后,我们可以摆脱错误检查。

perl -spe'open(STDOUT, ">", $o) if $. == 1' -- -o=file.out

奖励:如果您想从文件而不是 STDIN 中读取,以上所有方法都可以这样做。只需提供要从中读取的文件的名称作为后续参数。您甚至可以提供多个名称。