The stat preceding -l _ wasn't an lstat - perl

Look at two examples:
$ perl -e ' print -e "registrar" && -d _ ? "YES" : "NO" '
YES
$ perl -e ' print -e "registrar" && -l _ ? "YES" : "NO" '
The stat preceding -l _ wasn't an lstat at -e line 1.
-d and -l are both file test operators. Why second does not work? The stat preceding -l _ is same as for -d _.

Most of the file tests use stat, which follows symlinks; -l uses lstat because it makes no sense to follow one if you want to test if it itself is a symbolic link. Hence the error.
The diagnostics pragma can be used to give a more verbose explanation of errors and warnings, including this one:
$ perl -Mdiagnostics -E 'say -e "registrar" && -l _ ? "YES" : "NO" '
The stat preceding -l _ wasn't an lstat at -e line 1 (#1)
(F) It makes no sense to test the current stat buffer for symbolic
linkhood if the last stat that wrote to the stat buffer already went
past the symlink to get to the real file. Use an actual filename
instead.
Uncaught exception from user code:
The stat preceding -l _ wasn't an lstat at -e line 1.
If using perl 5.10 or newer, file tests can be directly chained without needing to be &&ed together. Doing this with -e and -l works since perl is smart enough to pick the appropriate stat function:
$ mkdir registrar
$ perl -E ' say -e -d "registrar" ? "YES" : "NO" '
YES
$ perl -E ' say -e -l "registrar" ? "YES" : "NO" '
NO
I actually need exists and not link. Is there short hand for that?
That shorthand notation only works for simple and-ing all the tests together, but you can call lstat directly to see if a file exists:
$ ln -s registrar registrar-link
$ perl -E 'say lstat "registrar" && ! -l _ ? "YES" : "NO" '
YES
$ perl -E 'say lstat "registrar-link" && ! -l _ ? "YES" : "NO" '
NO
$ perl -E 'say lstat "no-such-file" && ! -l _ ? "YES" : "NO" '
NO
And in a script as opposed to a one-liner, I'd use File::stat instead of the underscore notation.

Related

Why open function doesn't interpret the option of my command every time?

I want to use open function to run the following commands:
> echo "toto\ntata"
toto
tata
> echo -e "toto\ntata"
toto
tata
> echo -E "toto\ntata"
toto\ntata
> echo -n "toto\ntata"
toto\ntata
Note that for the last command, there is no trailing new line.
So I have the following script:
use strict;
sub run_cmd {
my ($cmd) = #_;
my $fcmd;
print("Run $cmd\n");
open($fcmd, "$cmd |");
while ( my $line = <$fcmd> ) {
print "-> $line";
}
close $fcmd;
}
eval{run_cmd('echo "toto\ntata"')};
eval{run_cmd('echo -e "toto\ntata"')};
eval{run_cmd('echo -E "toto\ntata"')};
eval{run_cmd('echo -n "toto\ntata"')};
But when I run it, I get this results:
Run echo "toto\ntata"
-> toto
-> tata
Run echo -e "toto\ntata"
-> -e toto
-> tata
Run echo -n "toto\ntata"
-> toto
-> tataRun echo -E "toto\ntata"
-> -E toto
-> tata
We can see the -n option is correctly interpreted by open, because there is no newline at the end of the text, but it is not the case for the options -e and -E. Worse, there are printed by echo.
Why my options are not interpreted by open every time? What should I do to get a correct output?
When you provide a shell command to system, qx, open, etc, it is treated as a sh shell command.
Here's the documentation for the echo builtin of dash which is used by some Linux distros as sh:
echo [-n] args...
Print the arguments on the standard output, separated by spaces. Unless the -n option is present, a newline is output following the arguments.
If any of the following sequences of characters is encountered during output, the sequence is not output. Instead, the specified action is performed:
...
\n Output a newline character.
...
It has no -e or -E option.
Running the commands from dash (aka sh in your case) results in exactly the same output as you received.
$ echo "toto\ntata"
toto
tata
$ echo -e "toto\ntata"
-e toto
tata
$ echo -E "toto\ntata"
-E toto
tata
$ echo -n "toto\ntata"
toto
tata$
Your test was probably run in the bash shell. The following will execute the command using bash instead:
open(my $pipe, "-|", "bash", "-c", $cmd)
Is there any reason why you use external program echo when perl has many ways to output some information to the console/file?
At your disposition you have print, printf, say
Perl input and output functions
Programming Perl

Converting frame.time to epoch

Using the following command what modifications need to be made to get frame.time as epoch in tshark?
tshark -T fields -n -r btle.pcap -E separator=, -e frame.time -e btle_rf.access_address_offenses -e btle_rf.channel -e btle_rf.flags -e btle_rf.flags.access_address_offenses_valid -e btle_rf.flags.channel_aliased -e btle_rf.flags.crc_checked -e btle_rf.flags.crc_valid -e btle_rf.flags.decrypted -e btle_rf.flags.dewhitened -e btle_rf.flags.mic_checked -e btle_rf.flags.mic_valid -e btle_rf.flags.noise_dbm_valid -e btle_rf.flags.reference_access_address_valid -e btle_rf.flags.rfu.1 -e btle_rf.flags.rfu.2 -e btle_rf.flags.signal_dbm_valid -e btle_rf.noise_dbm -e btle_rf.reference_access_address -e btle_rf.signal_dbm -e btle_rf.signed_byte_unused -e btle_rf.unsigned_byte_unused -e btle_rf.word_unused>btle.csv
As long as you are running Wireshark 1.4.0 or later, you can try changing -e frame.time to -e frame.time_epoch. If you're running an older version of Wireshark, then I might suggest upgrading to a newer version.
With the latest stable version of Wireshark (2.2.7 as of this writing), all frame filters are documented in View -> Internals -> Supported Protocols -> Frame -> ... and you can always reference the online Display Filter Reference page as well.

How to add a line in every file in a directory with perl commandline

I want to add a line at the beginning of every file in a directory.
perl -i.bkp -p -e 'print "#include top_level.reset\n" if $. == 1' *.reset
But this command is updating only the first file in the directory. I think this is because $. is not resetting for next file.
How to modify all the files.
You are correct, $. is not reset between files when processing #ARGV. See perlvar. You can work around it by explicitly closing ARGV on EOF - see eof. But I would not bother, instead I would use the shell to iterate the files:
for f in *.reset; do perl -i.bkp -p -e 'print "#include top_level.reset\n" if $. == 1' $f; done
ls -1 *.reset | xargs -n 1 perl -i.bkp -p -e 'print "#include top_level.reset\n" if $. == 1'

How to tell if my program is being piped to another (Perl)

"ls" behaves differently when its output is being piped:
> ls ???
bar foo
> ls ??? | cat
bar
foo
How does it know, and how would I do this in Perl?
In Perl, the -t file test operator indicates whether a filehandle
(including STDIN) is connected to a terminal.
There is also the -p test operator to indicate whether a filehandle
is attached to a pipe.
$ perl -e 'printf "term:%d, pipe:%d\n", -t STDIN, -p STDIN'
term:1, pipe:0
$ perl -e 'printf "term:%d, pipe:%d\n", -t STDIN, -p STDIN' < /tmp/foo
term:0, pipe:0
$ echo foo | perl -e 'printf "term:%d, pipe:%d\n", -t STDIN, -p STDIN'
term:0, pipe:1
File test operator documentation at perldoc -f -X.
use IO::Interactive qw(is_interactive);
is_interactive() or warn "Being piped\n";

Perl command line search and replace with multiple expressions

I am using Perl to search and replace multiple regular expressions:
When I execute the following command, I get an error:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g' -pe 's/(\W)##/\1/g'
syntax error at -e line 2, near "s/(\W)##/\1/g"
Execution of -e aborted due to compilation errors.
xargs: perl: exited with status 255; aborting
Having multiple -e is valid in Perl, then why is this not working? Is there a solution to this?
Several -e's are allowed.
You are missing the ';'
find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g;' -pe 's/(\W)##/\1/g;'
Perl statements has to end with ;.
Final statement in a block doesn't need a terminating semicolon.
So a single -e without ; will work, but you will have to add ; when you have multiple -e statements.
Having multiple -e values are valid, but is it useful? The values from the multiple -e are merely combined into one program, and it's up to you to ensure that together they make a syntactically correct program. The B::Deparse program can show you what perl thinks the program is:
$ perl -MO=Deparse -e 'print' -e 'q(Hello' -e ')'
print "Hello\n";
-e syntax OK
A curious thing to note is that a newline snuck in there. Think about how it got there to see what else perl is doing to combine multiple -e values.
In your program, you are substituting on the current line, then taking the modified line and substituting again. That's better written as:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g; s/(\W)##/\1/g'
Now, if you are building up this command line by adding more and more -e through some automated process and you don't know ahead of time what you get, maybe those -e make sense. However, you might consider that you can do the same thing to build up the string you give to -e. I don't know what might be better because you didn't explain why you are doing it that way.
But, I suspect that in some cases, people are actually thinking about having only one substitution work. They want to try one and if its pattern doesn't work, try a different one until one succeeds. In that case you don't want to separate the substitutions by semicolons. Use the short-circuiting || instead. The s/// returns the number of substitutions it made and || will stop (short circuit) when it finds a true value:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g || s/(\W)##/\1/g'
And note, you only need one -p. It only does its job once. Here's the program with multiple -p deparsed:
$ perl -MO=Deparse -i -pe 's/##(\W)/\1/g;' -pe 's/(\W)##/\1/g;'
BEGIN { $^I = ""; }
LINE: while (defined($_ = readline ARGV)) {
s/##(\W)/$1/g;
s/(\W)##/$1/g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
It's the same thing as having only one -p:
$ perl -MO=Deparse -pi -e 's/##(\W)/\1/g;' -e 's/(\W)##/\1/g;'
BEGIN { $^I = ""; }
LINE: while (defined($_ = readline ARGV)) {
s/##(\W)/$1/g;
s/(\W)##/$1/g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
Thanks so much! You helped me reduce my ascii / decimal / 8-bit binary table printer enough to fit in a tweet:
for i in {32..126};do printf "'\x$(printf %x $i)'(%3i) = " $i; printf '%03o\n' $i | perl \
-pe 's#0#000#g;' -pe 's#1#001#g;' -pe 's#2#010#g;' -pe 's#3#011#g;' \
-pe 's#4#100#g;' -pe 's#5#101#g;' -pe 's#6#110#g;' -pe 's#7#111#g' ; done | \
perl -pe 's#= 0#= #'