I have just tried using the following Perl script to do some text substitution using the File::Slurp module. It works fine on a single file given as an argument at the command line (BASH).
#!/opt/local/bin/perl -w
use File::Slurp qw( edit_file_lines );
foreach my $argnum (0 .. $#ARGV) {
edit_file_lines {
s/foo/bar/g;
print $_
}
$ARGV[$argnum];
}
I would like to alter it to cope also with pipes (i.e. STDIN), so that it can be in the middle of a series of piped operations:
for example:
command blah|....|my-perl-script|sort|uniq|wc....
What is the best way to change the Perl script to allow this, whilst retaining the existing ability to work with single files on the command line?
To have your script work in a pipeline, you could check if STDIN is connected to a tty:
use strict;
use warnings;
use File::Slurp qw( edit_file_lines );
sub my_edit_func { s/foo/bar/g; print $_ }
if ( !(-t STDIN) ) {
while(<>) { my_edit_func }
}
else {
foreach my $argnum (0 .. $#ARGV) {
edit_file_lines { my_edit_func } $ARGV[$argnum];
}
}
See perldoc -X for more information on the -t file test operator.
All you need is the following:
local $^I = '';
while (<>) {
s/foo/bar/g;
print;
}
This is basically the same as perl -i -pe's/foo/bar/', except it doesn't warn when STDIN is used.
Related
I am calling a perl script from a bash script. I want to obtain the return value that the perl script returns in the bash script in order to act on it. When I have the following, the output is a blank line to the console when I expect it to be "good".
bashThing.sh
#!/bin/bash
ARG="valid"
VAL=$(perl -I. -MperlThing -e "perlThing::isValid ${ARG}")
echo $VAL
perlThing.pm
#! /usr/bin/perl -w
use strict;
package perlThing;
sub isValid
{
my $arg = shift;
if($arg == "valid")
{
return "good";
}
else
{
return "bad";
}
}
1;
You didn't have Perl warnings enabled completely. You would have seen several warnings. I enabled warnings both in bash (using -w on the Perl one-liner), and in Perl with use warnings;. The shebang line is ignored in the .pm file since it is not being executed as a script.
isValid returned a string, but you were ignoring the returned value. To fix that, use print in the one-liner.
You also needed to pass the value to isValid properly.
Lastly, you need to use eq instead of == for string comparison.
bash:
#!/bin/bash
ARG="valid"
VAL=$(perl -w -I. -MperlThing -e "print perlThing::isValid(q(${ARG}))")
echo $VAL
Perl:
use strict;
use warnings;
package perlThing;
sub isValid
{
my $arg = shift;
if($arg eq "valid")
{
return "good";
}
else
{
return "bad";
}
}
1;
If you expect the value good or bad in $VAR, then you should print them not return them.
Other than printed stuff, the perl process can pass only integer return values to calling program. And you can check that using $? in bash script, this variable stores the return value of last run process.
## removesmalls.pl
#!/usr/bin/perl
use strict;
use warnings;
my $minlen = shift or die "Error: `minlen` parameter not provided\n";
{
local $/=">";
while(<>) {
chomp;
next unless /\w/;
s/>$//gs;
my #chunk = split /\n/;
my $header = shift #chunk;
my $seqlen = length join "", #chunk;
print ">$_" if($seqlen >= $minlen);
}
local $/="\n";
}
Exexecuting the script as follows:
perl removesmalls.pl 1000 contigs.fasta > contigs-1000.fasta
The above script works for me but there is a problem,
i have 109 different fasta files with different file names.
i can run the script for individual file but i want to run the script at once for all files and the result file should be individually different for each.
file names are like SRR8224532.fasta, SRR8224533.fasta, SRR8224534.fasta, and so on
i want the result files after removing the contigs (i.e., for me less than 1000) something like SRR8224532-out.fasta,
SRR8224533-out.fasta, and so on.
Any help or suggestion would be helpfull.
this is my Perl script
use strict;
use warnings;
use XML::Twig;
use Data::Dumper;
sub xml2array{
my $path = shift;
my $twig = XML::Twig->new->parsefile($path);
return map { $_ -> att('VirtualPath') } $twig -> get_xpath('//Signals');
}
sub compareMappingToArray {
my $mapping = shift;
my $signalsRef = shift;
my $i = 1;
print "In file : $mapping\n";
open(my $fh, $mapping);
while (my $r = <$fh>) {
chomp $r;
if ($r =~ /\'(ModelSpecific.*)\'/) {
my $s = $1;
my #matches = grep { /^$s$/ } #{$signalsRef};
print "line $i : not found - $s\n" if scalar #matches ==0;
print "line $i : multiple $s\n" if scalar #matches > 1;
}
$i = $i + 1 # keep line index
}
}
my $mapping = "C:/Users/HOR1DY/Desktop/Global/TA_Mapping/CAN/CAN_ESP_002_mapping.pm";
my #virtualpath = xml2array("SignalModel.xml");
compareMappingToArray($mapping, \#virtualpath);
The script works well, the aim of it is to compare the file "SignalModel.xml" and "CAN_ESP_002_mapping.pm" and putting the lines that didn't matches in a .TXT file. Here is how the .TXT file looks like:
In file : C:/Users/HOR1DY/Desktop/Global/TA_Mapping/CAN/CAN_ESP_002_mapping.pm
line 331 : not found - ModelSpecific.EID.NET.CAN_Engine.VCU.Transmit.VCU_202.R2B_VCU_202__byte_3
line 348 : not found - ModelSpecific.EID.NET.CAN_Engine.CMM_WX.Transmit.CMM_HYB_208.R2B_CMM_HYB_208__byte_2
line 368 : not found - ModelSpecific.EID.NET.CAN_Engine.VCU.Transmit.VCU_222.R2B_VCU_222__byte_0
But for this script, I put the two files that need to be compare inside of the code and instead of doing that, I would like to run the script in windows cmd line and having something like:
C:\Users>perl CANMappingChecker.pl -'file 1' 'file 2'
All the files are in .zip file so if I can execute the script that he goes inside and take the 2 files that I need for comparison, it should be perfect.
I really don't know how to do and what to put inside my script to make that in the cmd windows. Thanks for your help !
Program (or script) parameters are stored in the #ARGV array. shift and pop without any parameter will work on #ARGV when used outside of a sub, in a sub they operate on #_.
See Archive::Zip for zip file handling.
In the unix/linux version, I'd simply change the first line:
#!perl -i.bak
Using Activestate perl on windows, where I've created the association with .pl, I can run a perl script directly from the command line.
myScript.pl
How can I do inplace editing of files if I still want to use the default association?
Sounds like a trick question, and I wonder if I am understanding you right.
perl -pi.bak myScript.pl myfiletochange
Just call perl, supply the switches and the script name, and off you go.
Now, it may be that you do not want to supply these extra arguments. If so, you can simply set the variable $^I, which will activate the inplace edit. E.g.:
$^I = ".bak"; # will set backup extension
Since you are going to be using a script you might want to do something like this:
sub edit_in_place
{
my $file = shift;
my $code = shift;
{
local #ARGV = ($file);
local $^I = '';
while (<>) {
&$code;
}
}
}
edit_in_place $file, sub {
s/search/replace/;
print;
};
if you want to create a backup then change local $^I = ''; to local $^I = '.bak';
I am using ps -C <executable name> on Linux, but the same does not work on Windows.
How can I perform the same check in Perl so that it is platform independent?
You might be able to use Win32::Process::List
use 5.12.0;
use warnings;
use Win32::Process::List;
my $P = Win32::Process::List->new();
if($P->IsError == 1) {
die $P->GetErrorText;
}
my %list = $P->GetProcesses();
foreach my $key (keys %list) {
# $list{$key} = process name, $key=PID
say sprintf("%25s %10s", $list{$key}, $key);
}
And process appropriately.