Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

portability / system / elegance

by skendric (Initiate)
on Dec 04, 2016 at 13:19 UTC ( #1177153=perlquestion: print w/replies, xml ) Need Help??
skendric has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing semi-portable code (Linux / Windows).

I use 'system' to execute an external binary ('tshark', part of the Wireshark collection).

I handle slashes like this:

use File::Which qw(which); $tshark_binary = which('tshark'); $tshark_binary =~ s/\\/\\\\/g if $tshark_binary =~ /\\/;

The last line functions essentially as a "If this is Windows, escape the slashes in the path name", such that 'C:/Program Files/Wireshark/tshark.EXE' turns into 'C://Program Files//Wireshark//tshark.EXE'.

And then, when I actually execute tshark, I use the following:

system("\"$tshark_binary\" -r $pcap -e frame.number -e frame.time_epo +ch -e ip.src -e ip.id -T text -T fields -E separator=, > $temp_file") +;

The key 'portability' change being the escaped quotes around $tshark_binary, which aren't necessary in Linux but are needed under Windows, to dodge the: "C:\Program" is not an executable errors.

This is fine, and it works. But is there a more elegant / common way to accomplish these two tasks?

--sk

Replies are listed 'Best First'.
Re: portability / system / elegance (updated)
by haukex (Abbot) on Dec 04, 2016 at 15:58 UTC

    Hi skendric,

    In my experience, the best thing is to use modules to help you. A quick glance at the source of File::Which shows that it uses File::Spec internally, so the filenames that which is returning should already be in the native format. You can test this via use Data::Dumper; $Data::Dumper::Useqq=1; print Dumper($tshark_binary); (although keep in mind that the output will be in Perl's notation, so e.g. $VAR1 = "C:\\Foo\\Bar.txt" means that the string is actually C:\Foo\Bar.txt).

    As for calling external commands, I would not recommend the system(STRING) form, but instead the system(LIST) form (Update: that is, system called with more than one argument, or even better, the system PROGRAM LIST form), or even better, a module such as IPC::Run3, which allows you to dodge most shell quoting and escaping issues (on Linux, it can avoid the shell completely, and on Windows, it automatically uses Win32::ShellQuote).

    Unfortunately I can't test this on Windows at the moment, but I think this should work (it works on Linux): Updated: Tested and works on both Linux and Windows. On Windows, which('tshark') does indeed return "C:\\Program Files\\Wireshark\\tshark.EXE" as I suspected above. Also, as I mentioned below, if tshark is already in your PATH, you don't need the which, it works fine without (run3 ['tshark', ...) on both OSes.

    use File::Which qw/which/; use IPC::Run3 qw/run3/; run3 [which('tshark'), '-r', $pcap, qw/ -e frame.number -e frame.time_epoch -e ip.src -e ip.id -T text -T fields -E /,'separator=,'], undef, $temp_file;

    Although, if tshark is already in your $ENV{PATH}, why use which at all?

    Hope this helps,
    -- Hauke D

      I like this:

      Although, if tshark is already in your $ENV{PATH}, why use which at all?

      Just do a tshark command.

      If it fails then tshark was not in a path to an executable and you get "Command not found".

Re: portability / system / elegance
by ikegami (Pope) on Dec 05, 2016 at 01:40 UTC

    Avoid the shell. In addition to the usual benefits, this problem goes away.

    You should use IPC::Run3 or IPC::Run, but low-level IPC::Open3 will be easy enough to use here.

    use File::Spec::Functions qw( devnull ); use IPC::Open3 qw( open3 ); { my @cmd = ( $tshark_binary => ( -r => $pcap, -e => "frame.number", -e => "frame.time_epoch", -e => "ip.src", -e => "ip.id", -T => "text", -T => "fields", -E => "separator=," ), ); open(local *CHILD_STDIN, '<', devnull) or die $!; open(local *CHILD_STDOUT, '>', $file_temp) or die $!; my $pid = open3('<&CHILD_STDIN', '>&CHILD_STDOUT', '>&STDERR', @cmd); waitpid($pid, 0); }
Re: portability / system / elegance
by ww (Archbishop) on Dec 04, 2016 at 14:03 UTC

    Minimally more elegant (for some values of 'elegant'): use an alternative to the forward slash as a delimiter to change the path-separator in a code segment that ascertains the OS upon which the program is running (if ($^O eq 'MSWin32') { ... (adapted from below); } elsif ($^O eq 'linux') { ....

    D:\_STM_work>perl -E "my $foo='/path/path/somepgm.exe'; if ($^O eq 'MS +Win32') {$foo =~ s?/?\/\/?g;} say $foo;" //path//path//somepgm.exe

    Proofreading all the leaning toothpicks is a practice fraught with possibility of error; for elegance, stuff the separator changing into a sub which is called only when the script ascertains that the OS is some (recent) flavor of Windows.

      Would not the more "elegant" solution be to use a module that is designed for this purpose, File::Spec? From the documentation for the module, File::Spec requires an appropriate module (File::Spec::Cygwin, File::Spec::Unix, and File::Spec::Win32, among others), which alters such functions as catfile in an appropriate manner. Taking from ww's example (and setting the path value in a sub) using this code:

      perl -MFile::Spec -le 'sub tshark_path { my @filename = qw( / usr bin +tshark ); if ( $^O eq q{MSWin32} ) { @filename = ( q{c:}, q{Program F +iles}, q{Wireshark}, q{tshark.exe} ); } return @filename; } my @file += tshark_path(); print File::Spec->catfile( @file );'

      gives the following results when executed on Cygwin (under Win10), Win10, and linux:

      # Result under Cygwin /usr/bin/tshark # Result under Win10 (Powershell) C:\Program Files\Wireshark\tshark.exe # Result from linux /usr/bin/tshark

      Hope that helps.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1177153]
Approved by BrowserUk
Front-paged by Corion
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2018-01-20 16:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How did you see in the new year?










    Results (227 votes). Check out past polls.

    Notices?