Perl print formatting - perl

I would like to format(truncate/append with chars) a string to a specified length while printing in Perl.
For example
$string='my_string';
printf("%04s",$string);
should print
my_s
also if
$string='my';
I should get
00my
Is there any way to print last four characters ?
ring
and if string is
$string='my';
it should print
00my

You want to do this format string instead of yours:
printf ("%04.4s", $string);
You need the .4 because this specifies maximum length. (The 4 at the beginning specifies a minimum only)
here are the output of some tests:
$ perl -e "my \$string = \"my_string\";print sprintf(\"%04.4s\", 22);"
0022
$ perl -e "my \$string = \"my_string\";print sprintf(\"%04.4s\", \$string);"
my_s
$ perl -e "my \$string = \"my\";print sprintf(\"%04s\", \$string);"
00my
Here is the output using the wrong format string. As you can see strings are not truncated.
$ perl -e "my \$string = \"my_string\";print sprintf(\"%04s\", 22);"
0022
$ perl -e "my \$string = \"my_string\";print sprintf(\"%04s\", \$string);"
my_string

printf('%04s', substr($_, 0, 4));

Related

perl - How to print utf8 code points for each byte

I'm trying to print the code points for all possible byte values.
My test file :
$ perl -e ' open($fh,">raw_bytes.dat");while($i++<256){ print $fh chr($i-1) } close($fh)'
$ ls -l raw_bytes.dat
-rw-rw-r--+ 1 uuuuu Domain Users 256 Mar 20 15:41 raw_bytes.dat
$
What should go into the below #---> part so that I print the code points of utf8 $x in hexadecimal?
perl -e ' use utf8; open($fh,"<raw_bytes.dat");binmode($fh);
while($rb=read($fh,$x,1)) { utf8::encode($x);
#--->
} '
I tried %02x using printf, but it didn't work. Also, I want the solution only using core modules.
Use unpack('H*'):
$ perl -e '$x="\x80"; utf8::encode($x); print unpack("H*", $x), "\n"'
c280
For your example file I get
$ perl -e 'open($fh, "<", "raw_bytes.dat"); binmode($fh);
while ($rb=read($fh,$x,1)) { utf8::encode($x);
print unpack("H*", $x), "\n";
}'
00
01
02
03
...
7f
c280
c281
c282
c283
...
c3bd
c3be
c3bf
Variants:
$ perl -e '$x="\x80"; utf8::encode($x);
print uc(unpack("H*", $x)), "\n"'
C280
$ perl -e '$x="\x80"; utf8::encode($x);
($r = uc(unpack("H*", $x))) =~ s/(..)/\\X\1/g;
print "$r\n"'
\XC2\X80
# a little bit pointless example, but assume that $x is a provided Perl scalar....
$ perl -e '$x="\N{U+0080}\N{U+0081}";
printf("U+%04x ", ord($_)) foreach(split(//, $x));
print "\n";'
U+0080 U+0081
Please remember the difference between
a scalar holding a raw string: split(//) returns octets, e.g. \x80
a scalar holding a properly encoded string: split(//) returns characters, e.g. \N{U+0080}
I tried %02x using printf, but it didn't work.
You can use
printf "%vX\n", $x;
According to perldoc sprintf:
vector flag
This flag tells Perl to interpret the supplied string as a vector of
integers, one for each character in the string. Perl applies the
format to each integer in turn, then joins the resulting strings with
a separator (a dot . by default). This can be useful for displaying
ordinal values of characters in arbitrary strings.

Perl -e field substitution

I am using perl -e to convert a hexadecimal number(523cc261) to a meaningful date:
perl -e 'my $t=localtime 0x523cc261; print $t . "\n"'
Fri Sep 20 21:47:13 2013
However i am not able to script it as above code needs the value to be provided on prompt.I tried substituting 523cc261 with a variable but it does not work:
b=523cc261
perl -e 'my $t=localtime 0x`echo b`; print $t . "\n"`
Backticks found where operator expected at -e line 1, near "0x`echo b`"
(Missing operator before `echo b`?)
syntax error at -e line 1, near "0x`echo b`"
My question is how to provide the decimal value(523cc261) via argument in a script.
Easiest way will be to pass the time to the Perl script as an argument. I've rewritten the script to be a little more concise, too:
% b=523cc261
% perl -E 'say scalar localtime hex $ARGV[0]' $b
Fri Sep 20 14:47:13 2013
You can use the ENV HASH :
$ b=523cc261 perl -le 'my $t = scalar localtime hex $ENV{"b"}; print $t;'
Another solution (a bit obfuscated, $b is a shell variable) :
$ b=523cc261 perl -le 'my $t = scalar localtime hex "'$b'"; print $t;'

Why the decode results of different string are same?

$ perl -MMIME::Base64 -e 'print decode_base64("c3luX2Fja0AxNjMuY29t");'
syn_ack#163.com
$ perl -MMIME::Base64 -e 'print decode_base64("AHN5bl9hY2tAMTYzLmNvbQ");'
syn_ack#163.com
The encode string are different, but the decode results are same, why?
Your second string AHN5bl9hY2tAMTYzLmNvbQ decodes to:
�syn_ack#163.com
The unusual character in first position might not be printed by your terminal software.
Contrary to what you said, they are not the same:
$ perl -MMIME::Base64 -E'
say
decode_base64("c3luX2Fja0AxNjMuY29t") eq
decode_base64("AHN5bl9hY2tAMTYzLmNvbQ") ?1:0'
0
So what are they?
$ perl -MData::Dumper -MMIME::Base64 -e'
$Data::Dumper::Useqq = 1;
print Dumper(decode_base64("c3luX2Fja0AxNjMuY29t"))'
$VAR1 = "syn_ack\#163.com";
$ perl -MData::Dumper -MMIME::Base64 -e'
$Data::Dumper::Useqq = 1;
print Dumper(decode_base64("AHN5bl9hY2tAMTYzLmNvbQ"))'
$VAR1 = "\0syn_ack\#163.com";

How can I let perl interpret a string variable that represents an address

I want to feed input to a C program with a perl script like this
./cprogram $(perl -e 'print "\xab\xcd\xef";').
However, the string must be read from a file. So I get something like this:
./cprogram $(perl -e 'open FILE, "<myfile.txt"; $file_contents = do { local $/; <FILE> }; print $file_contents'. However, now perl interprets the string as the string "\xab\xcd\xef", and I want it to interpret it as the byte sequence as in the first example.
How can this be achieved? It has to be ran on a server without File::Slurp.
In the first case, you pass the three bytes AB CD EF (produced by the string literal "\xAB\xCD\xEF") to print.
In the second case, you must be passing something other than those three bytes to print. I suspect you are passing the twelve character string \xAB\xCD\xEF to print.
So your question becomes: How does one convert the twelve-character string \xAB\xCD\xEF into the three bytes AB CD EF. Well, you'd require some kind of parser such as
s/\\x([0-9a-fA-F][0-9a-fA-F])|\\([^x])|([^\\]+)/
$1 ? chr(hex($1)) : $2 ? $2 : $3
/eg
And here it is at work:
$ perl -e'print "\\xAB\\xCD\\xEF";' >file
$ echo -n "$( perl -0777pe'
s{\\x([0-9a-fA-F][0-9a-fA-F])|\\([^x])|([^\\]+)}{
$1 ? chr(hex($1)) : $2 // $3
}eg;
' file )" | od -t x1
0000000 ab cd ef
0000003
Is Perl's eval too evil? If not, end in print eval("\"$file_contents\"");
Or can you prepare the file in advance using Perl? EG print FILE "\xAB\xCD\xED"; then read the resulting file with your existing code.
using a bash trick:
perl -e "$(echo "print \"$(cat input)"\")"
which for your example becomes:
./cprogram "$(perl -e "$(echo "print \"$(cat myfile.txt)"\")")"

variable for field separator in perl

In awk I can write: awk -F: 'BEGIN {OFS = FS} ...'
In Perl, what's the equivalent of FS? I'd like to write
perl -F: -lane 'BEGIN {$, = [what?]} ...'
update with an example:
echo a:b:c:d | awk -F: 'BEGIN {OFS = FS} {$2 = 42; print}'
echo a:b:c:d | perl -F: -ane 'BEGIN {$, = ":"} $F[1] = 42; print #F'
Both output a:42:c:d
I would prefer not to hard-code the : in the Perl BEGIN block, but refer to wherever the -F option saves its argument.
To sum up, what I'm looking for does not exist:
there's no variable that holds the argument for -F, and more importantly
Perl's "FS" is fundamentally a different data type (regular expression) than the "OFS" (string) -- it does not make sense to join a list of strings using a regex.
Note that the same holds true in awk: FS is a string but acts as regex:
echo a:b,c:d | awk -F'[:,]' 'BEGIN {OFS=FS} {$2=42; print}'
outputs "a[:,]42[:,]c[:,]d"
Thanks for the insight and workarounds though.
You can use perl's -s (similar to awk's -v) to pass a "FS" variable, but the split becomes manual:
echo a:b:c:d | perl -sne '
BEGIN {$, = $FS}
#F = split $FS;
$F[1] = 42;
print #F;
' -- -FS=":"
If you know the exact length of input, you could do this:
echo a:b:c:d | perl -F'(:)' -ane '$, = $F[1]; #F = #F[0,2,4,6]; $F[1] = 42; print #F'
If the input is of variable lengths, you'll need something more sophisticated than #f[0,2,4,6].
EDIT: -F seems to simply provide input to an automatic split() call, which takes a complete RE as an expression. You may be able to find something more suitable by reading the perldoc entries for split, perlre, and perlvar.
You can sort of cheat it, because perl is actually using the split function with your -F argument, and you can tell split to preserve what it splits on by including capturing parens in the regex:
$ echo a:b:c:d | perl -F'(:)' -ane 'print join("/", #F);'
a/:/b/:/c/:/d
You can see what perl's doing with some of these "magic" command-line arguments by using -MO=Deparse, like this:
$ perl -MO=Deparse -F'(:)' -ane 'print join("/", #F);'
LINE: while (defined($_ = <ARGV>)) {
our(#F) = split(/(:)/, $_, 0);
print join('/', #F);
}
-e syntax OK
You'd have to change your #F subscripts to double what they'd normally be ($F[2] = 42).
Darnit...
The best I can do is:
echo a:b:c:d | perl -ne '$v=":";#F = split("$v"); $F[1] = 42; print join("$v", #F) . "\n";'
You don't need the -F: this way, and you're only stating the colon once. I was hoping there was someway of setting variables on the command line like you can with Awk's -v switch.
For one liners, Perl is usually not as clean as Awk, but I remember using Awk before I knew of Perl and writing 1000+ line Awk scripts.
Trying things like this made people think Awk was either named after the sound someone made when they tried to decipher such a script, or stood for AWKward.
There is no input record separator in Perl. You're basically emulating awk by using the -a and -F flags. If you really don't want to hard code the value, then why not just use an environmental variable?
$ export SPLIT=":"
$ perl -F$SPLIT -lane 'BEGIN { $, = $ENV{SPLIT}; } ...'