如何使用"here-doc"将行打印到文件?



基本上,这是我编程和使用Google过去半小时的结果,试图实现一件简单的事情:从STDIN中获取用户输入并将它们写入结构化XML文件中作为输出。下面是我丑陋的代码:

#!/bin/perl
print "img URL = ?    ";
$img = <>;
chomp($img);
print "filename = ?    ";
$filename = <>;
chomp($filename);
# now write xml with inserted contents to a file:
open (CARDFILE, ">>$filename");
print CARDFILE "<card>rn";
print CARDFILE "    <img>$img</img>rn";
print CARDFILE "    <type>$type</type>rn";
print CARDFILE "    <expansion>$expansion</expansion>rn";
print CARDFILE "    <keyword>$keyword</keyword>rn";
print CARDFILE "    <color>$color</color>rn";
print CARDFILE "    <linkcolor>$linkcolor</linkcolor>rn";
print CARDFILE "    <kickercolor>$kickercolor</kickercolor>rn";
print CARDFILE "    <cost>$cost</cost>rn";
print CARDFILE "    <strength>$strength</strength>rn";
print CARDFILE "    <health>$health</health>rn";
print CARDFILE "</card>";
close (CARDFILE);  

我是Perl的新手,但我可以说出我的代码中的一些重大问题:

  1. 使用两行首先接收STDIN然后chomp()它以摆脱尾随回车。应该有单行吗?

  2. 我无法弄清楚如何使用"here-doc"的东西一次性打印多行文件。如果没有 CARDFILE 文件句柄,我可以使用 here-doc 语法将所有字符串打印到单个打印语句中STDOUT。现在我必须多次调用打印函数。

  3. 对于这些"rn",有没有办法使用不同的功能,这样我就不必记得在每行之后手动插入它们?

读一行

阅读单行的成语是

chomp(my $var = <>);

但我通常最终会写一个小的prompt子例程:

sub prompt {
  print @_;
  chomp(my $answer = <>);
  return $answer;
}
...;
my $img = prompt("img URL = ? ");

这里-文档

here-doc 只是另一种字符串文字,例如

my $str = <<"END";
foo bar
baz
END
print $str;

如果用单引号将分隔符括起来:<<'END' ,则无法将变量插入到 here-doc 中。请注意,结束标记必须始终位于行的开头,但可以包含空格字符:

    my $string = <<'END THIS';
    foo
    bar
END THIS
    # marker must be at start of line!

您可以直接打印像 here 文档

print CARDFILE <<"END";
<card>
    <img>$img</img>
    <type>$type</type>
    <expansion>$expansion</expansion>
    <keyword>$keyword</keyword>
    <color>$color</color>
    <linkcolor>$linkcolor</linkcolor>
    <kickercolor>$kickercolor</kickercolor>
    <cost>$cost</cost>
    <strength>$strength</strength>
    <health>$health</health>
</card>
END

不同之处在于,行尾没有rn,除非保存带有 Windows 行尾的源文件。但是,here-doc 确实包含物理换行符,并且将始终以换行符终止。

打印东西

print 函数打印由$,特殊变量分隔的参数,然后输出$变量。默认情况下,这两个值均为空。但是我们可以提供一个临时值:

local $ = "123";
print "foo";

输出:

foo123

当然,我们通常会将其设置为 n .但是有一个快捷方式:只需在源代码的开头放一个use feature 'say'use 5.010(或更高(,然后您就可以

say "foo";

它会自动附加换行符。

打开文件句柄

使用词法变量。使用三参数打开。进行适当的错误处理。这意味着:

use autodie;
open my $fh, "<", "filename";

my $filename = "filename";
open my $fh, "<", $filename or die "Can't open $filename: $!";

第二个参数是模式>>追加。显式指定它更安全,而不是作为文件名的一部分。

一般说明

始终use strict; use warnings;脚本的顶部,这有助于发现问题。

人们在Perl中遇到的最大问题是他们忘记了行尾的分号。

这里的文档有两种风格。如果您在此处的文档名称周围使用双引号(或无(,它将插入变量等。

如果您在"此处文档"名称两边使用单引号,则不会进行插值。仔细观察分号和引号的位置。

#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);
my $foo = "The value of foo";
my $bar = "The value of bar";
my $heredoc =<<"END_HERE_1";
This is my first here document
and this will interpolate things
like $foo and $bar.
Heck, it even shows up correctly
in Vim.
END_HERE_1
say "$heredoc"; # All set
print  <<'END_HERE_2';
This is my first here document
and this will NOT interpolate things
like $foo and $bar.
Heck, it even shows up correctly
in Vim.
END_HERE_2
say "And that's all folks!";

如果你想要一个here-document,你可以使用:

#!/usr/bin/env perl
use strict;
use warnings;
print "img URL = ?    ";
chomp(my $img = <>);
print "filename = ?   ";
chomp(my $filename = <>);
# now write xml with inserted contents to a file:
open my $CARDFILE, ">>", $filename or die "Failed to open file $filename for writing";
my $type = "unregimented";
my $expansion = "contracted";
my $keyword = "lock";
my $color = "green";
my $linkcolor = "blue";
my $kickercolor = "red";
my $cost = '$0.00';
my $strength = "humungous";
my $health = "excellent";
print $CARDFILE <<EOF;
<card>r
    <img>$img</img>r
    <type>$type</type>r
    <expansion>$expansion</expansion>r
    <keyword>$keyword</keyword>r
    <color>$color</color>r
    <linkcolor>$linkcolor</linkcolor>r
    <kickercolor>$kickercolor</kickercolor>r
    <cost>$cost</cost>r
    <strength>$strength</strength>r
    <health>$health</health>r
</card>r
EOF
close $CARDFILE;

请注意压缩的输入,数据现在垂直排列 - 尽管运行了几次Perl脚本,我宁愿它从命令行参数中获取数据,而不是提示它。

具有讽刺意味的是,如果使用此处文档,则无法使用输出记录分隔符来指定行尾。 无需执行任何特殊操作,即可设置:

$ = "rn";

并且每个print输出将以 CRLF 结尾。 ($ 的默认值为 undef ,因此默认情况下不会打印任何内容作为结束的行。 或者,您可以通过以下方式使代码更易于理解:

use English '-no_match_vars';
$ORS = "rn";

例如:

#!/usr/bin/env perl
use strict;
use warnings;
use English '-no_match_vars';
print "img URL = ?    ";
my $img = <>;
chomp($img);
print "filename = ?    ";
my $filename = <>;
chomp($filename);
# now write xml with inserted contents to a file:
open my $CARDFILE, ">>", $filename or die "Failed to open file $filename for writing";
my $type = "unregimented";
my $expansion = "contracted";
my $keyword = "lock";
my $color = "green";
my $linkcolor = "blue";
my $kickercolor = "red";
my $cost = '$0.00';
my $strength = "humungous";
my $health = "excellent";
$ORS = "rn";
print $CARDFILE "<card>";
print $CARDFILE "    <img>$img</img>";
print $CARDFILE "    <type>$type</type>";
print $CARDFILE "    <expansion>$expansion</expansion>";
print $CARDFILE "    <keyword>$keyword</keyword>";
print $CARDFILE "    <color>$color</color>";
print $CARDFILE "    <linkcolor>$linkcolor</linkcolor>";
print $CARDFILE "    <kickercolor>$kickercolor</kickercolor>";
print $CARDFILE "    <cost>$cost</cost>";
print $CARDFILE "    <strength>$strength</strength>";
print $CARDFILE "    <health>$health</health>";
print $CARDFILE "</card>";
close $CARDFILE;

你可以做

print CARDFILE qq{<card>
<img>$img</img>
<type>$type</type>};

等等。 qq告诉Perl插入字符串,所以它与做一个"多行字符串"是一样的。

最新更新