1

This is a follow-up question from Modify text column based on the column before it

I wanna change the starting index of the line processing, say start from the third line. I notice that in order for perl to use the variable in shell, I must export the variable and use $ENV{} in perl, see:

#!/bin/bash

t=3 
export t 
perl -e 'print $ENV{t}'
perl -lane '$F[3] += sin($F[2]/10 * 4 * atan2 1, 1) if($ENV{t} .. 4);
print "@F" 
'test.txt > test_new.txt

Here test.txt is merely the same with the previous question:

A 0.016333 0.003203 0.472723
A 0.016333 0.035228 0.472723
B 0.016333 0.067253 0.472723 
B 0.016333 0.099278 0.472723 
C 0.016333 0.131303 0.472723 
C 0.016333 0.163328 0.472723

However, the $ENV{t} does not work at all: the line processing still starts from the first line. Maybe in IF statement the usage is different?? What should I do to control which line to start?

4
  • try if ( $. >= $ENV{t} ) Commented Jun 15, 2016 at 4:11
  • if ( $a .. $b ) is always FALSE for any integer values. Commented Jun 15, 2016 at 4:13
  • 1
    perldoc.perl.org/perlvar.html search for $. or INPUT_LINE_NUMBER Commented Jun 15, 2016 at 4:14
  • The construct TT=123 perl -lane '...... $ENV{TT} ....' foo.txt will also work for you. Commented Jun 15, 2016 at 4:18

2 Answers 2

3

It's the range operator that's doing it. The particular rule you are using for (3..4) is

If either operand of scalar ".." is a constant expression, that operand is considered true if it is equal (== ) to the current input line number (the $. variable).

Otherwise,

It is false as long as its left operand is false. Once the left operand is true, the range operator stays true until the right operand is true, AFTER which the range operator becomes false again. It doesn't become false till the next time the range operator is evaluated.

When you have a variable for one end point it is being evaluated and is found to be true. So the left end is always true and the operator never gets to be false, and all lines are printed.

As for how to do it, forego the elegance and test explicitly,

if $. >= $ENV{t} and $. <= 4

You can still use the range operator, for a more compact expression

if $.==$ENV{t} .. 4

However, at this point this may be not as clear as a normal test while a tiny gain in performance (if any) may not even be measurable. Thanks to ikegami for bringing this up and for further comments.

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks. but is there a way to control which line to start with?
@James I got pulled away from this, but wanted at least to answer the question. I'll add to it in a few mintues.
I just solved it using $. It would be great if you know better ways and post them!
@James Glad to see you worked it out! I've also added that to my answer. What do you consider "better" ways? More concise or shorter or ....?
Using the range operator, it would look like if $.==$ENV{t} .. 4.
|
0
#!/bin/bash

t=3  
export t  
perl -e 'print $ENV{t}' 
perl -lane '$F[3] += sin($F[2]/10 * 4 * atan2 1, 1) if(($.>=$ENV{t})&&($.<= 4)); 
print "@F" 'test.txt > test_new.txt

The above code works! It is great to know the current line number is $.

The result is:

A 0.016333 0.003203 0.472723
A 0.016333 0.035228 0.472723
B 0.016333 0.067253 0.493849581177725
B 0.016333 0.099278 0.503907047205915
C 0.016333 0.131303 0.472723
C 0.016333 0.163328 0.472723

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.