Passing bash variable to perl one-liners - insights required - perl

I am fairly new to Perl and I have been toying with one-liners to get some file operations done. I am using Perl to print segment between defined by line numbers which are obtained from another file. My current issue is as follows:
export var=10 ; perl -ne 'print $_ if $. == $ENV{var}' filename.txt
prints line number 10, but if i want to print from line 10 to the end of file, i tried
export var=10 ; perl -ne 'print if $ENV{var} .. -1' filename.txt
--fails. The output generated prints the whole file. Additionally, the following works,
export var=10 ; perl -ne 'print if $. >= $ENV{var} $$ $. <= $ENV{var}+5 ' filename.txt
But since i am dealing with a variable file length after the required line, this is not a viable solution.

Perl flip-flop operator has some of his own warts (like is my variable line number or boolean?), so when in doubt do explicit comparison to $. line number.
export var=10 ; perl -ne 'print if $.== $ENV{var} .. -1' filename.txt

You don't need to use Environmental variables:
var=10; echo "$(seq 20 35)" | perl -lne 'print if $. >= '"$var"';'
29
30
31
32
33
34
35
Take a look at the way I escaped $var
Using flip-flop:
var=10; echo "$(seq 20 35)" | perl -lne 'print if $.== '"$var"' .. -1;'
29
30
31
32
33
34
35

From line 10 to the end of the file:
export var=10 ; perl -ne 'print $_ if $. > $ENV{var}' filename.txt
If you want it to include line 10:
export var=10 ; perl -ne 'print $_ if $. >= $ENV{var}' filename.txt

Related

What's the equivalent of sed -n 'np' file in perl

Via sed,one can obtain the n'th line from a file.
sed -n '40p' file;
A=$(sed -n '40p' file);
what is the equivalent for this in perl?
perl -ne '$. ==40 and print' file
or you can skip lines less than 40, print desired line, and stop file processing,
perl -ne 'next if $. <40; print; last' file
This could be one
perl -ne 'print if $. == 40'
$. is the line number of current input stream.

Is __LINE__ constant-folded in this Perl one-liner?

In exploring an alternative answer to sarathi's current file line number question, I wrote this one-liner with the expectation that it would print the first line of all files provided:
$ perl -ne 'print "$ARGV : $_" if __LINE__ == 1;' *txt
This did not work as expected; all lines were printed.
Running the one-liner through -MO=Deparse shows that the conditional is not present. I assume this is because it has been constant-folded at compile time:
$ perl -MO=Deparse -ne 'print "$ARGV : $_" if __LINE__ == 1;' *txt
LINE: while (defined($_ = <ARGV>)) {
print "$ARGV : $_";
}
-e syntax OK
But why?
Run under Perl 5.8.8.
__LINE__ corresponds to the line number in the Perl source, not in the input file.
__LINE__ is the source line number i.e., the program line number.
$. will give you the input file line number.
if you want to print all the first lines of all the files then you can try this:
perl -lne '$.=0 if eof;print $_ if ($.==1)' *.txt

s3cmd list of contents - only filenames - perl one liner?

Currently I'm using s3cmd ls s3://location/ > file.txt to get a list of contents of my s3 bucket and save on a txt. However the above returns dates, filesizes paths and filenames.
for example:
2011-10-18 08:52 6148 s3://location//picture_1.jpg
I only need the filenames of the s3 bucket - so on the above example I only need picture_1.jpg.
Any suggestions?
Could this be done with a Perl one liner maybe after the initial export?
Use awk:
s3cmd ls s3://location/ | awk '{ print $4 }' > file.txt
If you have filenames with spaces, try:
s3cmd ls s3://location/ | awk '{ s = ""; for (i = 4; i <= NF; i++) s = s $i " "; print s }' > file.txt
File::Listing does not support this format because the designers of this listing format were stupid enough to not simply reuse an existing one. Let's parse it manually instead.
use URI;
my #ls = (
"2011-10-18 08:52 6148 s3://location//picture_1.jpg\n",
"2011-10-18 08:52 6148 s3://location//picture_2.jpg\n",
"2011-10-18 08:52 6148 s3://location//picture_3.jpg\n",
);
for my $line (#ls) {
chomp $line;
my $basename = (URI->new((split q( ), $line)[-1])->path_segments)[-1];
}
__END__
picture_1.jpg
picture_2.jpg
picture_3.jpg
As oneliner:
perl -mURI -lne 'print ((URI->new((split q( ), $line)[-1])->path_segments)[-1])' < input
I am sure a specific module is the safer option, but if the data is reliable, you can get away with a one-liner:
Assuming the input is:
2011-10-18 08:52 6148 s3://location//picture_1.jpg
2011-10-18 08:52 6148 s3://location//picture_2.jpg
2011-10-18 08:52 6148 s3://location//picture_3.jpg
...
The one-liner:
perl -lnwe 'print for m#(?<=//)([^/]+)$#'
-l chomps the input, and adds newline to end of print statements
-n adds a while(<>) loop around the script
(?<=//) lookbehind assertion finds a double slash
...followed by non-slashes to the end of the line
The for loop assures us that non-matches are not printed.
The benefit of the -n option is that this one-liner may be used in a pipe, or on a file.
command | perl -lnwe '...'
perl -lnwe '...' filename

awk or perl one-liner to print line if second field is longer than 7 chars

I have a file of 1000 lines, each line has 2 words, separated by a space. How can I print each line only if the last word length is greater than 7 chars? Can I use awk RLENGTH? is there an easy way in perl?
#OP, awk's RLENGTH is used when you call match() function. Instead, use the length() function to check for length of characters
awk 'length($2)>7' file
if you are using bash, a shell solution
while read -r a b
do
if [ "${#b}" -gt 7 ];then
echo $a $b
fi
done <"file"
perl -ane 'print if length($F[1]) > 7'
You can do:
perl -ne '#a=split/\s+/; print if length($a[1]) > 7' input_file.txt
Options used:
-n assume 'while () { ... }' loop around program
-e 'command' one line of program (several -e's allowed, omit programfile)
You can use the auto-split option as used by Chris
-a autosplit mode with -n or -p (splits $_ into #F)
perl -ane 'length $F[1] > 7 && print' <input_file>
perl -lane 'print if (length($F[$#F]) > 7)' fileName
or
perl -pae '$_ = "" if (length($F[$#F]) <= 7)' fileName

How can I use a Perl one-liner to decode an ASCII string encoded in hex?

I’d like to write a Perl one-liner to decode a line of ASCII characters encoded as hexadecimal numbers (for example the line 48 54 54 50 should be decoded as HTTP). I came up with this:
perl -nE 'say map(chr, map { qq/0x$_/ } split)'
It prints an empty line. What am I doing wrong and how would you write it?
It's your qq/0x$_/ trick that doesn't work. chr expects a number as argument, but gets the string literal "0x48". Use the hex function to convert 48 to a decimal number, like datageist does in his answer.
This works for me:
echo '48 54 54 50' | perl -nE 'say map(chr, map { hex } split)'
This works:
echo '48 54 54 50' | perl -nE 'say map{chr(hex)} split'
I’m assuming you want to feed the data from STDIN.
As always with Perl TIMTOWTDI.
I thought I would submit several options, and show what they would look like if they were written normally. If you want to know more about the command line options perldoc perlrun is a useful resource.
These all output the same thing. With the exception that some of them don't print a newline on the end.
echo '48 54 54 50' | perl -0x20 -pe'$_=chr hex$_'
echo '48 54 54 50' | perl -0x20 -ne'print chr hex$_'
echo '48 54 54 50' | perl -0777 -anE'say map chr,map hex,#F'
echo '48 54 54 50' | perl -0777 -anE'say map{chr hex$_}#F'
echo '48 54 54 50' | perl -0apple'$_=chr hex$_' -0x20
echo '48 54 54 50' | perl -apple'$_=join"",map{chr hex}#F'
echo '48 54 54 50' | perl -lanE'say map{chr hex}#F'
The following is what some of the examples would look like if they were written normally. If you want to figure out what the rest of them do, definitely look at perldoc perlrun.
perl -0x20 -pe'$_=chr hex$_'
This is one is fairly straight forward. It is perhaps the best example here, and is also the shortest one. It pretends that spaces are used to separate lines, so that there is only one letter to deal with inside of the loop.
# perl -0x20 -pe'$_=chr hex$_'
$/ = " "; # -0 ( input separator )
while( <> ){
$_ = chr hex $_;
} continue {
print $_;
}
perl -0apple'$_=chr hex$_' -0x20
This one has a few command line options that don't do anything useful.
The first -0 option is there so that -l sets the output separator to an empty string.
Which is actually the default for the output separator.
There are two -p options where one would have sufficed.
The -a option sets up the #F array, but we don't actually use it.
Basically I used -a -l and a second -p so that the options would spell apple. Otherwise this one is the same as the last example.
echo '48 54 54 50' | perl -0x20 -pe'$_=chr hex$_'
# perl -0apple'$_=chr hex$_' -0x20
$/ = ""; # -0 ( input separator )
$\ = $/; # -l ( output separator )
$/ = " "; # -0x20 ( input separator )
while( <> ){
#F = split " ", $_; # -a ( unused )
$_ = chr hex $_;
} continue {
print $_;
}
perl -lanE'say map{chr hex}#F'
I figured I already spelled apple, I might as well spell lanE.
-l isn't really useful, because we already are using say.
Used -E instead of -e so that we could use say.
# perl -lanE'say map{chr hex}#F'
$\ = $/; # -l ( output separator set to "\n" )
while( <> ){
#F = split " ", $_; # -a
say map { chr hex $_ } #F;
}
Play perlgolf?
-ple y/0-9A-Fa-f//cd;$_=pack"H*",$_
-ple $_=pack"H*",$_,join"",split
-nE say map chr hex,split
-naE say map chr hex,#F