What's the equivalent of sed -n 'np' file in perl - 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.

Related

Performing a one-liner on multiple input files specified by extension

I'm using the following line to split and process a tab-delimited .txt file:
perl -lane 'next unless $. >30; #array = split /[:,\/]+/, $F[2]; print if $array[1]/$array[2] >0.5 && $array[4] >2' input.txt > output.txt
Is there a way to alter this one-liner in order to perform this on multiple input files without specifying each individually?
Ideally this would be accomplished by performing it on all files within the current directory holding the .txt (or other) file extension - and then outputting a set of modified files names e.g.:
Input:
test1.txt
test2.txt
Output:
test1MOD.txt
test2MOD.txt
I know that I can access the filename to modify it with $ARGV but I do not know how to go about getting it to run on multiple files.
Solution:
perl -i.MOD -lane 'next unless $. >30; #array = split /[:,\/]+/, $F[2]; print if $array[1]/$array[2] >0.5 && $array[4] >2; close ARGV if eof;' *.txt
$. needs to be reset otherwise it throws a division by zero error.
If you don't mind slightly different output file name,
perl -i.MOD -lane'
next unless $. >30;
#array = split /[:,\/]+/, $F[2];
print if $array[1]/$array[2] >0.5 && $array[4] >2;
close ARGV if eof; # Reset $. for each file.
' *.txt
Have you considered calling the perl script from a shell for loop?
for TXT in *.txt; do
OUT=$(basename $TXT .txt)MOD.txt
perl ... $TXT > $OUT
done

Passing bash variable to perl one-liners - insights required

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

How to add blank line after every grep result using Perl?

How to add a blank line after every grep result?
For example, grep -o "xyz" may give something like -
file1:xyz
file2:xyz
file2:xyz2
file3:xyz
I want the output to be like this -
file1:xyz
file2:xyz
file2:xyz2
file3:xyz
I would like to do something like
grep "xyz" | perl (code to add a new line after every grep result)
This is the direct answer to your question:
grep 'xyz' | perl -pe 's/$/\n/'
But this is better:
perl -ne 'print "$_\n" if /xyz/'
EDIT
Ok, after your edit, you want (almost) this:
grep 'xyz' * | perl -pe 'print "\n" if /^([^:]+):/ && ! $seen{$1}++'
If you don’t like the blank line at the beginning, make it:
grep 'xyz' * | perl -pe 'print "\n" if /^([^:]+):/ && ! $seen{$1}++ && $. > 1'
NOTE: This won’t work right on filenames with colons in them. :)½
If you want to use perl, you could do something like
grep "xyz" | perl -p -e 's/(.*)/\1\n/g'
If you want to use sed (where I seem to have gotten better results), you could do something like
grep "xyz" | sed 's/.*/\0\n/g'
This prints a newline after every single line of grep output:
grep "xyz" | perl -pe 'print "\n"'
This prints a newline in between results from different files. (Answering the question as I read it.)
grep 'xyx' * | perl -pe '/(.*?):/; if ($f ne $1) {print "\n"; $f=$1}'
Use a state machine to determine when to print a blank line:
#!/usr/bin/env perl
use strict;
use warnings;
# state variable to determine when to print a blank line
my $prev_file = '';
# change DATA to the appropriate input file handle
while( my $line = <DATA> ){
# did the state change?
if( my ( $file ) = $line =~ m{ \A ([^:]*) \: .*? xyz }msx ){
# blank lines between states
print "\n" if $file ne $prev_file && length $prev_file;
# set the new state
$prev_file = $file;
}
# print every line
print $line;
}
__DATA__
file1:xyz
file2:xyz
file2:xyz2
file3:xyz

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

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