How do you use the 7z test? - perl

The directory of the file is defined.
I want to get the 'test' value using the 'test' command of 7z.
foreach $str0 (glob "*.zip"){
my $test = system( "7z t -y $str0");
print $test;
}
How can I get the '7z test' value?
Edit:
Do you mean to use qx instead of System?
Is it right that you meant it?
foreach $str0 (glob "*.zip"){
my $test = qx/7z t -y $str0/;
print $test;
}
or
foreach $str0 (glob "*.zip"){
my $test = `7z t -y $str0`;
print $test;
}
I tried both, but I can't get the 'test value'.

There are at least three ways to do this in Perl; the qx and backticks ways are basically the same thing, except with qx you have a choice of what delimiter to use to start/end the string. Using ' as the delimiter prevents variable interpolation.
The third way is using open to open a pipe to 7zip.
#with qx
my $test = qx/7z t -y $str0/;
#with backticks
my $test = `7z t -y $str0`;
#with open
open my $pipe, '-|', '7z', 't', '-y', $str0;
my $test = join "\n", readline $pipe;
close $pipe;

Related

perl : making a script as efficient as a perl one-liner

I'm able to do this on the command line and it works :
~/Tools/perl/edif_extr_cell.pl design.edif nmos1p8v | perl -p -e 's/^/\n/ if /portImplementation|figure\s+device/;' | perl -n -000 -e 'print if /portImplementation/;'
(basically, extracting a section of the EDIF file).
Now, I want to make a utility of this. And my script is below. Question : can this code be more efficient? If feel like it's very inelegant. I could pipe streams easily on the command line but, in a script, I feel lost.
#!/usr/bin/perl -w -p
BEGIN{ $file = '';}
s/^/\n/ if /portImplementation|figure\s+device/;
$file .= $_;
END{
$cmd = q{\rm -f /tmp/dump}.$$.'.txt';
system( $cmd );
open( OUT, ">/tmp/dump$$.txt");
print OUT $file;
close OUT;
$out = `perl -n -000 -e 'print if /portImplementation/;' /tmp/dump$$.txt`;
system( $cmd );
print $out;
}
If I understand correct, you want to be able to do
~/Tools/perl/edif_extr_cell.pl design.edif nmos1p8v | myfilter
Ideally, you'd merge the two Perl scripts into one rather than having one script launch two instances of Perl, but this turns out to be rather hard because of the change to $/ (via -00) and because you insert newlines in the first filter.
The simplest answer:
#!/bin/sh
perl -pe's/^/\n/ if /portImplementation|figure\s+device/' |
perl -00ne'print if /portImplementation/'
It appears that you were trying to write the equivalent of that sh script in Perl. It would look like the following:
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open qw( open3 );
# open3 has issues with lexical file handles.
pipe(local *PIPE_READER, local *PIPE_WRITER)
or die($!);
my $pid1 = open3('<&STDIN', '>&PIPE_WRITER', '>&STDERR',
'perl', '-pes/^/\n/ if /portImplementation|figure\s+device/');
my $pid2 = open3('<&PIPE_READER', '>&STDOUT', '>&STDERR',
'perl', '-00neprint if /portImplementation/');
waitpid($pid1);
waitpid($pid2);
I'd normally recommend IPC::Run3 or IPC::Run for launching and interfacing with child processes, but low-level open3 does the trick nicely in this particular situation.
I downloaded a random EDIF file from GitHub, running the following script on it gives the same output as your code:
#! /usr/bin/perl
use warnings;
use strict;
my #buffer;
my $found;
my $prepend = q();
while (<>) {
if (/portImplementation|figure\s+device/) {
if ($found && #buffer) {
print $prepend, #buffer;
$prepend = "\n";
}
undef $found;
#buffer = ();
}
$found ||= /portImplementation/;
push #buffer, $_;
}
# Don't forget to output the last paragraph!
print $prepend, #buffer if $found && #buffer;

unix functions inside perl

I tried to use some unix tools inside a perl driver script because I knew little about writing shell script. My purpose is to just combine a few simple unix commands together so I can run the script on 100 directories in one perl command.
The task is I have more than 100 folders, in each folder, there are n number of files. I want to do the same thing on each folder, which is to combine the files in them and sort the combined file and use bedtools to merge overlapping regions (quite common practice in bioinformatics)
Here is what I have:
#!/usr/bin/perl -w
use strict;
my $usage ="
This is a driver script to merge files in each folder into one combined file
";
die $usage unless #ARGV;
my ($in)=#ARGV;
open (IN,$in)|| die "cannot open $in";
my %hash;
my $final;
while(<IN>){
chomp;
my $tf = $_;
my #array =`ls $tf'/.'`;
my $tmp;
my $tmp2;
foreach my $i (#array){
$tmp = `cut -f 1-3 $tf'/'$i`;
$tmp2 = `cat $tmp`;
}
my $tmp3;
$tmp3=`sort -k1,1 -k2,2n $tmp2`;
$final = `bedtools merge -i $tmp3`;
}
print $final,"\n";
I know that this line isn't working at all..
$tmp2 = `cat $tmp`;
The issue is how to direct the output into another variable in perl and use that variable later on in another unix command...
Please let me know if you can point out where I can change to make it work. Greatly appreciated.
The output from backticks usually includes newlines, which usually have to be removed before using the output downstream. Add some chomp's to your code:
chomp( my #array =`ls $tf'/.'` );
my $tmp;
my $tmp2;
foreach my $i (#array){
chomp( $tmp = `cut -f 1-3 $tf'/'$i` );
chomp( $tmp2 = `cat $tmp` );
}
my $tmp3;
chomp( $tmp3=`sort -k1,1 -k2,2n $tmp2` );
$final = `bedtools merge -i $tmp3`;
To use a perl variable in the shell, this is an example :
#!/usr/bin/env perl
my $var = "/etc/passwd";
my $out = qx(file $var);
print "$out\n";
For the rest, it's very messy. You should take the time learning perl and not mixing coreutils commands and Perl, where perl itself is a better tool to do the whole joke.
OK. I gave it up on perl and decided to give it a try using shell script. It worked!!
Thanks for the above answers though!
for dir in `ls -d */`
do
name=$(basename $dir /)
cd $dir
for file in `ls`
do
cut -f 1-3 $file > $file.tmp
done
for x in `ls *tmp`
do
cat $x >> $name.tmp1
done
sort -k1,1 -k2,2n $name.tmp1 > $name.tmp2
bedtools merge -i $name.tmp2 > $name.combined
done

Perl - Using backquotes missing output

Hello guys i need to capture the output of an external command, herefore I use backquotes.
However when the command reaches a newline the output is ommitted. Where $_ = AD
#lines = `"C:/Program Files/Veritas/NetBackup/bin/admincmd/bppllist" $_ -U"`
Test: test1
Test: test2
Test: test3
Test: test4
The actual output:
#lines
Test: test1
Test: test2
Thank you for your time.
print HTML "<h2 id='pol'>Policy Configuration\n</h2>" ;
#bpllist =`"$admincmd/bppllist.exe"` or die print "$admincmd/bppllist.exe not found or could not be executed";
foreach (#bpllist)
{
print HTML "<div><table class='table'>\n";
#lines = `"$admincmd/bppllist" $_ -U` or die print "$admincmd/bpplinfo $_ -U not found or could not be executed";
print HTML "\t<tr>\n\t<td><b>Policy name: <b></td><td>$_</td>\n\t</tr>\n" ;
foreach (#lines) {
chop;
($var, $value) = split(/:/,$_,2);
$var = "" if !defined($var);
$value = "" if !defined($value);
print HTML "\t<tr>\n\t<td>$var</td><td>$value</td>\n\t</tr>\n" ;
}
print HTML "</table></div>";
}
The output of #bpllist:
AD
Sharepoint
Echchange
Vmware
Here's how to capture the STDOUT & STDERR of a spawned process using backticks:
my $output = join('', `command arg1 arg2 arg3 2>&1`);
How it works has no dependence whatsoever on newlines in the output of command.
If you also need to send text to command's STDIN, then use IPC::Open3.
Cleaned your code up a bit. It works for me.
use strict;
use warnings;
use 5.10.0;
# something missing here to set up HTML file handle
# something missing here to set up $admincmd
print HTML q{<h2 id='pol'>Policy Configuration\n</h2>};
my #bpllist = `"$admincmd/bppllist.exe"`
or die "$admincmd/bppllist.exe not found or could not be executed\n";
for my $policy (#bpllist) {
print HTML q{<div><table class='table'>\n};
my #lines = `$admincmd/bpplinfo.exe $policy -U 2>&1`;
print HTML qq{\t<tr>\n\t<td><b>Policy name: <b></td><td>$policy</td>\n\t</tr>\n} ;
for my $pair (#lines) {
chomp($pair); # only remove newlines, not other characters
my ($var, $value) = split /:/, $pair, 2;
$var //= '';
$value //= '';
print HTML qq{\t<tr>\n\t<td>$var</td><td>$value</td>\n\t</tr>\n} ;
}
print HTML q{</table></div>};
}
Update 2
You appear to be doing this on windows?
I don't think the 2>&1 trick will work there.
Instead of using qx or backticks and then shell commands to redirect output, give the core module, IPC::Cmd, a try. In particular, its exportable function &run will conveniently capture both STDOUT and STDERR for you. From the synopsis:
### in list context ###
my( $success, $error_message, $full_buf, $stdout_buf, $stderr_buf ) =
run( command => $cmd, verbose => 0 );
Maybe the command send its output to stderr.
Try this:
my $output = `'command' -ARG -L 2>&1`;
regards,

One-liner Perl command to rename files

So far this one-liner is stripping off one line and renaming the file, but I need help to alter it so that it strips that line I am looking for Data for and remove the old file extension .csv instead of adding to it. (.csv.out). I am not sure if this can be done with one-liner.
Instead it's adding on the the extension filename.csv.out
Example
test_20110824.csv.out
One-liner:
find -type f -name '*.csv' -exec perl -i.out -wlne '/^Data for/ or print' {} \;
I want to replace the extension:
test_20110824.out
perl -MFile::Copy -we 'for (glob "*.csv") { my ($name) = /^(.+).csv/i; move($_, $name . ".out"); }'
To remove the header matching Data for:
perl -MFile::Copy -MTie::File -wE 'for (glob '*x.csv') { tie my #file,
"Tie::File", $_ or die $!; shift #file if $file[0] =~ /^Data for/;
untie #file; my ($name) = /^(.*).csv/i; move($_, $name . ".out"); }'
But then it's really not a one-liner anymore...
use strict;
use warnings;
use Tie::File;
use File::Copy;
use autodie;
for (#ARGV) {
tie my #file, "Tie::File", $_;
shift #file if $file[0] =~ /^Data for/;
untie #file;
my ($name) = /^(.*).csv/i;
move($_, $name . ".out");
}
And use with:
$ script.pl *.csv
A simple Bash shell script will suffice
(shopt -s failglob; for i in *.csv.out; do echo mv $i ${i%csv.out}out; done)
The shopt -s failglob is needed to ensure that if there are no matches the command will fail instead of trying to rename *.csv.out to *.out. The construct ${i%csv.out}out removes a trailing csv.out and replaces it with just out.
As I have coded it here, this will just echo the commands it would execute. When you're satisfied it does what you want, remove the word echo.

get output of execution perl

Usually to get the output of a command I run from perl I use back tick
my $value = `pwd`;
How do I do it though if I need to insert a variable within the back ticks ``?
Text inside backticks is interpolated before it is passed to the operating system in the same way as text inside double quotes. So these statements all do what they look like they do:
$value = `$command`;
$value = `$someCommand $arg`;
$value = `$someOtherCommand #list`;
qx() is another way of running an external command and returning the output. If for some reason you don't want Perl to interpolate your command, you can run qx with the single-quote delimiter.
$value = qx'echo $PATH'; # shell's $PATH, not Perl's $PATH
You can just insert it. E.g.
my $dir = "/home"
my $text = `ls -l $dir`;
print $text;
my $hello = "world";
my $value = ` echo $hello `;
print $value;
Use qx() instead of backticks. Eg. my ($used, $dir); ($used) = qx(du -k $dir);