Perl backticks when using pipes - perl

I'm experiencing some problems trying to capture the output of a simple command:
$timeTotal = `echo $timeTotal + $time | bc -l`;
But I'm getting the following errors:
sh: +: not found
sh: Syntax error: "|" unexpected
This command works perfectly in bash but it seems sh is being actually used. At the very beginning I thought that the problem is the pipe usage (although the sum is not well interpreted neither). What confuses me is that the following command in the same script causes no error and works properly:
my $time = `cat $out.$step | bc -l`;
Any suggestions?

$timeTotal contains a trailing newline it shouldn't, so you're executing
echo XXX
and
+ YYY | bc -l
instead of
echo XXX + YYY | bc -l
You're surely missing a chomp somewhere.
There's also a double-quote in your command that's out of place.

The backticks are deprecated. Use the qx(..) syntax instead.
$timeTotal = qx(echo $timeTotal + $time | bc -l");

Related

calling awk from perl does not work with unzip redirect

The below command works fine when I run it manually but when I call it from perl script using backticks or system command, it gives this error
sh: -c: line 0: syntax error near unexpected token `('
Script snapshot:
#Find contents of myFile in zipfile and output the matched records to output.txt
$cmd = "awk -F\"|\" 'NR==FNR{hash[\$0]=1;next} \$237 in hash' $myFILE <(unzip -p $zipfile *XYZ*) >> output.txt";
$result=`$cmd`;
It seems that we cannot call a subshell i.e. (unzip ...) within a system call through perl. Please advise as I have been struggling since a couple of days.
This is because the command line is most likely /bin/bash and perl's shell is /bin/sh which does not support this mean. Put the zip command before the awk with pipe (|). Something like this:
#Find contents of myFile in zipfile and output the matched records to output.txt
$cmd = "unzip -p $zipfile *XYZ* | awk -F\"|\" 'NR==FNR{hash[\$0]=1;next} \$237 in hash' $myFILE >> output.txt";
$result=`$cmd`;

Executing shell command with pipe in perl

I want the output of the shell command captured in variable of a perl script, only the first section of the command before the pipe "|" is getting executed, and there is no error while executing the script
File.txt
Error input.txt got an error while parsing
Info output.txt has no error while parsing
my $var = `grep Error ./File.txt | awk '{print $2}'`;
print "Errored file $var";
Errored file Error input.txt got an error while parsing
I want just the input.txt which gets filtered by awk command but not happening. Please help
The $ in $2 is interpolated by Perl, so the command that the shell receives looks like:
grep Error ./File.txt | awk '{print }'
(or something else if you have recently matched a regular expression with capture groups). The workaround is to escape the dollar sign:
my $var = `grep Error ./File.txt | awk '{print \$2}'`
Always include use strict; and use warnings; in EVERY perl script.
If you had, you'd have gotten the following warning:
Use of uninitialized value $2 in concatenation (.) or string at scratch.pl line 4.
This would've alerted you to the problem in your command, namely that the $2 variable is being interpolated instead of being treated like a literal.
There are three ways to avoid this.
1) You can do what mob suggested and just escape the $2
my $var = `grep Error ./File.txt | awk '{print \$2}'`
2) You can use the qx form of backticks with single quotes so that it doesn't interpolate, although that is less ideal because you are using single quotes inside your command:
my $var = qx'grep Error ./File.txt | awk \'{print $2}\''
3) You can just use a pure perl solution.
use strict;
use warnings;
my ($var) = do {
local #ARGV = 'File.txt';
map {(split ' ')[1]} grep /Error/, <>;
};

Using pipe when executing command in perl

I am trying to use following command in perl but it giving me error
system("zcat myfile.gz | wc > abc.txt");
But when i run this I am getting error
syntax error near unexpected token `|'
Even if I remove >abc.txt I am still getting error.
Can we use pipe with system command?
Here are error details:
sh: -c: line 1: syntax error near unexpected token `|'
sh: -c: line 1: ` | wc '
Next time, test your demo program to make sure it actually exhibits the behaviour you said it does. You actually ran something closer to
while (my $file_name = <>) {
system("zcat $file_name | wc > abc.txt");
}
There are two errors in that:
You didn't remove the trailing newline, so the shell was trying to execute
zcat def.gz
| wc >abc.txt
instead of
zcat def.gz | wc >abc.txt
You didn't transform the file name into a shell literal before emdedding it your command.
Consider what would happen if the file name contained a space. You would be executing
zcat def ghi.gz | wc >abc.txt
instead of
zcat 'def ghi.gz' | wc >abc.txt
Solution:
use String::ShellQuote qw( shell_quote );
while (my $file_name = <>) {
chomp($file_name);
system("zcat -- ".shell_quote($file_name)." | wc > abc.txt");
}
It is working as expected:
perl -lne 'system("cat *.java|wc");'
Something odd with your filename, maybe.
You could check the interpolation of your shell like this:
my #file = `ls -1 myfile*.gz`;chomp(#files);
print join("\n",#files);
There are other possibilites to execute in perl, like backtick, open with |, qx.
If you are trouble with filenames, you could get the filenames by yourself and call the system in a specific way to avoid executing shell: http://docstore.mik.ua/orelly/perl/cookbook/ch19_07.htm
If there is only one scalar argument, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing (this is /bin/sh -c on Unix platforms, but varies on other platforms). If there are no shell metacharacters in the argument, it is split into words and passed directly to execvp , which is more efficient.
I got this error message when trying to use backticks to execute a pipeline that included a cut -d \| command. Turns out I had to double escape the pipe character eg cut -d \\|

Perl wget quotes syntax issue

thank you for reading.
For a shell command to wget, something like this works:
wget -q -O - http://www.myweb.com | grep -oe '\w*.\w*#\w*.\w*.\w\+' | sort -u
However, when I try to insert that command inside the Perl program, then I get a syntax error referring to "backslashes found where operator expected, bareword found where operator expected". So I replaced the quotes that surround the regex by this {} but, what that does is just like commenting it out, it does not bring the error, but it is as if the regex weren't, so obviously the curly braces are a wrong attempt.
This is the code, it is inside a foreach:
foreach(#my_array) {
$browser->get($_);
# and here below is where the error comes
system ('wget -q -O -"$_" | grep -oe '\w*.\w*#.\w*.\w\+' | sort -u');
If I replace the single quotes wrapping the regex by {}, then wget does get the URLs but the grep command does not act.
So that is the issue, how to resolve the quotes annoying the syntax
You are using single-quotes ' in your system call. They do not fill in variables for you. The $_ is not getting replaced. Also, the single quotes next to the grep make this invalid syntax.
Try this instead:
system ("wget -q -O - $_ | grep -oe '\w*.\w*\#.\w*.\w\+' | sort -u");
You can also use the qq operator:
system ( qq( wget -q -O - $_ | grep -oe '\w*.\w*\#.\w*.\w\+' | sort -u) );
Also, look at perlop.
Another thought: If you have $browser object that can get() the url, why do you need to use wget? You could also do this in Perl.
You want this:
system ("wget -q -O -\"$_\" | grep -oe '\\w*.\\w*#.\\w*.\\w\\+' | sort -u");
You can include what you like within double quotes, only you have to escape certain characters.
Incidentally, Perl's qq() operator might interest you. You can look it up.

Pulling hostname from TNS entry

I am working on a script that will need to determine which node a db being used by a local app is running on. I've been trying to use this as a chance to force myself to learn awk/sed and have a test script to test the statements. It's working off a copy of the tnsnames.ora file I have moved to the home folder the script is located in.
Here is a valid tnsnames.ora stanza:
(
DESCRIPTION = (
ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (Host=iplab)(Port=1521))
)
(CONNECT_DATA=(SID=spurs1))
)
After doing some research and getting the awk expression to pull the tns entry to $host I came up with the below script but it doesn't seem to work.
#!/bin/ksh
db=spurs
host=$(awk -v db=$db "/${db}/ {for(i=1; i<=5; i++) {getline; print}}" tnsnames.ora)
echo $host
host= $host | sed 's/Host\s=\s\([a-z]+[0-9]?\)/\1/'
echo $host
When I run it the awk statement I get the following:
(DESCRIPTION = (ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (Host=hostname)(Port=1521))) (CONNECT_DATA=(SID=spurs1)) )
./tns.ksh: line 6: (DESCRIPTION: not found
(DESCRIPTION = (ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (Host=hostname)(Port=1521))) (CONNECT_DATA=(SID=spurs1)) )
From what I have seen reading tutorials and forums I think sed is setup correctly and should be setting $host to one or more lowercase letters followed by 0 or 1 numbers after Host = . Since (DESCRIPTION is the start of $host before and after the sed statement I'm not sure how it isn't finding it, an
This worked for me:
tnsping $db | grep HOST | cut -d\ -f 14 | sed 's/).*//g'
On my system I can use this to get the host as long as the host name doesn't have an equals sign (or the actual literal word HOST in the name of the host):
echo $TNS_STRING | sed 's/.HOST//g' | sed 's/).//g' | sed 's/=//g' | sed 's/\s*//g'
Your value for $host is likely a multiline value, so you need to quote it anyplace you use it, i.e.
host=$(awk -v db=$db "/${db}/ {for(i=1; i<=5; i++) {getline; print}}" tnsnames.ora)
echo "$host"
You also need to capture the output (using command-substitution) via $(...)
host=$(echo "$host" | sed 's/Host\s=\s\([a-z]+[0-9]?\)/\1/')
echo "$host"
(and echo it), so it can be processed by sed
Revise
host=$(echo $host | sed 's/.*Host=//; s/).*$//)
echo "$host"
I've switched back to just $host, without the dbl-quotes, as you don't want the linebreaks in the data. Now it is all one big string, and the regex, strips every upto host=, and then strips everything after the first remaining ) char.
If you still get error messages, I don't have access to a tnsnames.ora record, so please edit your query to include a valid record.
I hope this helps.
you may be better relying on the output of tnsping instead of parsing the file: tnsping appears to emit the description on one line:
host=$(
tnsping $db | while read line; do
if [[ "$line" == *HOST* ]]; then
s=${line#*HOST=}; s=${s%%)*}; echo "$s"; break
fi
done
)
This might work for you:
db=spurs
host=$(sed '/^(/,/^)/!d;/^(/{h;d};H;/^)/!d;g;/'"$db"'/!d;s/.*Host=\([^)]*\).*/\1/' tnsnames.ora)
Tested Code:
OIFS=$IFS;
IFS="(";
tns=`tnsping TNS_ALIAS`
tns_arr=($tns);
tns_info=(`(for ((i=0; i<${#tns_arr[#]}; ++i)); do echo "${tns_arr[$i]/)/}"; done)| grep 'HOST\|PORT'|sed 's/)//g'|sed 's/ //g'`)
for ((i=0; i<${#tns_info[#]}; ++i)); do eval "export ${tns_info[$i]}"; done
echo "host:" $HOST
echo "port:" $PORT
IFS=$OIFS;