Basically, this is the result of my programming and using Google for the last half an hour trying to achieve a simple thing: pick up user inputs from STDIN
and write them into a structured XML file as the output. Below is my ugly code:
#!/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>\r\n";
print CARDFILE " <img>$img</img>\r\n";
print CARDFILE " <type>$type</type>\r\n";
print CARDFILE " <expansion>$expansion</expansion>\r\n";
print CARDFILE " <keyword>$keyword</keyword>\r\n";
print CARDFILE " <color>$color</color>\r\n";
print CARDFILE " <linkcolor>$linkcolor</linkcolor>\r\n";
print CARDFILE " <kickercolor>$kickercolor</kickercolor>\r\n";
print CARDFILE " <cost>$cost</cost>\r\n";
print CARDFILE " <strength>$strength</strength>\r\n";
print CARDFILE " <health>$health</health>\r\n";
print CARDFILE "</card>";
close (CARDFILE);
I am new to Perl, but I could tell some of the significant problems in my code:
Use two lines to first receive STDIN
then chomp()
it to get rid of the trailing carriage return. Should there be a one-liner?
I cannot work out how to use the "here-doc" thing to print multiple-lines to file in one go. Without the CARDFILE
file handle, I can use here-doc syntax to print all strings to STDOUT
in a single print statement. Now I have to call the print function multiple times.
For these "\r\n"
s, is there a way to use a different function so I don't have to remember to manually insert them after each line?
An idiom for reading a single line would be
chomp(my $var = <>);
But I usually end up writing a small prompt
subroutine:
sub prompt {
print @_;
chomp(my $answer = <>);
return $answer;
}
...;
my $img = prompt("img URL = ? ");
A here-doc is just another kind of string literal, e.g.
my $str = <<"END";
foo bar
baz
END
print $str;
If you enclose the delimiter with single quotes: <<'END'
, then you can't interpolate variables into the here-doc. Note that the end marker must always be at the start of the line, but may include space characters:
my $string = <<'END THIS';
foo
bar
END THIS
# marker must be at start of line!
You can directly print a here-doc like
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
The difference is here that there will be no \r\n
at the end of line, except when you save the source file with Windows line endings. However, the here-doc does include the physical newlines, and will always terminate with a newline.
The print
function prints the arguments seperated by the $,
special variable, and then outputs the $\
variable. Both of these are empty by default. But we could provide a temporary value:
local $\ = "123";
print "foo";
Output:
foo123
Of course, we would usually set it to \n
. But there is a shortcut: Just put a use feature 'say'
or use 5.010
(or higher) at the start of the source code, then you can
say "foo";
which automatically appends a newline.
Use lexical variables. Use three-arg open. Do proper error handling. This means either:
use autodie;
open my $fh, "<", "filename";
or
my $filename = "filename";
open my $fh, "<", $filename or die "Can't open $filename: $!";
The second argument is the mode, which would be >>
for appending. It is safer to specify it explicitly, and not as part of the filename.
Always use strict; use warnings;
at the top of your script, this helps finding problems.
The big issue people have with here files in Perl is they forget the semicolon on the end of the line.
Here documents come in two flavors. If you use double quotes (or none) around the here document name, it will interpolate variables and such.
If you used single quotes around the Here Document name, it won't interpolate. Carefully observe the placement of semicolons and quotation marks.
#! /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!";
If you want a here-document, you can use:
#!/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;
Note the compressed input, with the data now lining up vertically — though, having run the Perl script a few times, I'd far rather it took its data from command line arguments than prompted for it.
Ironically, if you use a here document, you cannot use the output record separator to specify the line ending. Without doing anything special, you can set:
$\ = "\r\n";
and each print
output will be ended with CRLF. (The default value of $\
is undef
, so nothing is printed as a line ending by default.) Alternatively, you can make your code more comprehensible with:
use English '-no_match_vars';
$ORS = "\r\n";
For example:
#!/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 = "\r\n";
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With