How to do index parsing in expect script - perl

I have a code from perl which I need to convert to .expect script to check if -T is present and then use the next one as a timeout.. basically check the arg count, see if -T is one of them.
Perl code:
# check command line against valid arguments
%opt=();
unless( getopts('T:', \%opt) )
{
print("$progname : Illegal command line arguments\n");
exit(1);
}
$commandTimeout=$opt{T} if $opt{T};
$inputCommand = join(" ", #ARGV); # convert arguments into one (long) string

Expect uses Tcl which is also a general purpose programming language. You can go through the command line arguments by your self.
[user#host] # cat foo.tcl
for {set i 0} {$i < [llength $argv]} {incr i} {
puts "argv\[$i]=[lindex $argv $i]"
}
foreach arg $argv {
puts "$arg"
}
[user#host] # tclsh foo.tcl hello world
argv[0]=hello
argv[1]=world
hello
world
[user#host] #
For more info, see Tcl's doc.

Related

Separate command-line arguments into two lists and pass to programs (shell)

What I want to do is take a list of command-like arguments like abc "def ghi" "foo bar" baz (note that some arguments are quoted because they contain spaces), and separate them out into two lists of arguments which then get passed to other programs that are invoked by the script. For example, odd-numbered arguments to one program and even-numbered arguments to another program. It is important to preserve proper quoting.
Please note, I need a solution in pure Bourne Shell script (i.e., sh not bash or such). The way I'd do this in Bash would be to use arrays, but of course the Bourne Shell doesn't have support for arrays.
At the cost of iterating over the original arguments twice, you can define a function that can run a simple command using only the even or odd arguments. This allows us to use the function's arguments as an additional array.
# Usage:
# run_it <cmd> [even|odd] ...
#
# Runs <cmd> using only the even or odd arguments, as specified.
run_it () {
cmd=${1:?Missing command name}
parity=${2:?Missing parity}
shift 2
n=$#
# Collect the odd arguments by discarding the first
# one, turning the odd arguments into the even arguments.
if [ $# -ge 1 ] && [ $parity = odd ]; then
shift
n=$((n - 1))
fi
# Repeatedly move the first argument to the
# to the end of the list and discard the second argument.
# Keep going until you have moved or discarded each argument.
while [ "$n" -gt 0 ]; do
x=$1
if [ $n -ge 2 ]; then
shift 2
else
shift
fi
set -- "$#" "$x"
n=$((n-2))
done
# Run the given command with the arguments that are left.
"$cmd" "$#"
}
# Example command
cmd () {
printf '%s\n' "$#"
}
# Example of using run_it
run_it cmd even "$#"
run_it cmd odd "$#"
This might be what you need. Alas, it uses eval. YMMV.
#!/bin/sh
# Samples
foo() { showme foo "$#"; }
bar() { showme bar "$#"; }
showme() {
echo "$1 args:"
shift
local c=0
while [ $# -gt 0 ]; do
printf '\t%-3d %s\n' $((c=c+1)) "$1"
shift
done
}
while [ $# -gt 0 ]; do
foo="$foo \"$1\""
bar="$bar \"$2\""
shift 2
done
eval foo $foo
eval bar $bar
There's no magic here -- we simply encode alternating arguments with quote armour into variables so they'll be processed correctly when you eval the line.
I tested this with FreeBSD's /bin/sh, which is based on ash. The shell is close to POSIX.1 but is not necessarily "Bourne". If your shell doesn't accept arguments to shift, you can simply shift twice in the while loop. Similarly, the showme() function increments a counter, an action which can be achieved in whatever way is your favourite if mine doesn't work for you. I believe everything else is pretty standard.

Calling one file from another file gives print value instead of return return value in perl

I can execute one Perl file from another Perl file using the command
`perl -X file-path -arguments=value`
but this gives the print value from the file instead of giving the return value.
This is abc.pl:
my $a = `perl -X def.pl -num=4`;
print "here a is $a";
This is def.pl:
print "in def file";
my $xy = "hello world!";
return $xy;
When invoke the file as perl -X abc.pl, I expect the output to be
here a is hello world!
but I am getting
Can't return outside a subroutine at def.pl line 3.
here a is in def file
How can I achieve this?
-X suppresses warnings. Don't suppress them, they give the hint why the code is failing your expectations.
You should be using subroutines and libraries.
def.pl
sub hello {
print "in def file\n";
my $xy = "hello world!";
return $xy;
}
1;
abc.pl
require './def.pl';
my $a = hello();
print "here a is $a";
Next step up from here is organising your code as modules.
When you use the `command` in Perl, the result is the standard output of the called command. This is similar to shell similar syntax.
Given 'def.pl' is perl program, you can execute it directly from abc.pl, using 'require'. However, 'def.pl' must be a valid perl program. Executing 'return $xy' outside a function will result in error message: Can't return outside a subroutine at - line 1.
Consider creating a function in 'def.pl' that returns the value of $xy. The alternative, which is less than ideal, is to use global.
# abc.pl
require 'def.pl' ;
my $a = xy() ;
print "here a is $a";
# def.pl
sub xy {
my $xy = "..." ;
return $xy ;
}
Alternatively, you can make def.pl 'print' the result, making it possible to retrieve the 'result' from the def.pl script.
# def.pl
warn "in def file";
my $xy = "hello world!";
print $xy;

Get value of autosplit delimiter?

If I run a script with perl -Fsomething, is that something value saved anywhere in the Perl environment where the script can find it? I'd like to write a script that by default reuses the input delimiter (if it's a string and not a regular expression) as the output delimiter.
Looking at the source, I don't think the delimiter is saved anywhere. When you run
perl -F, -an
the lexer actually generates the code
LINE: while (<>) {our #F=split(q\0,\0);
and parses it. At this point, any information about the delimiter is lost.
Your best option is to split by hand:
perl -ne'BEGIN { $F="," } #F=split(/$F/); print join($F, #F)' foo.csv
or to pass the delimiter as an argument to your script:
F=,; perl -F$F -sane'print join($F, #F)' -- -F=$F foo.csv
or to pass the delimiter as an environment variable:
export F=,; perl -F$F -ane'print join($ENV{F}, #F)' foo.csv
As #ThisSuitIsBlackNot says it looks like the delimiter is not saved anywhere.
This is how the perl.c stores the -F parameter
case 'F':
PL_minus_a = TRUE;
PL_minus_F = TRUE;
PL_minus_n = TRUE;
PL_splitstr = ++s;
while (*s && !isSPACE(*s)) ++s;
PL_splitstr = savepvn(PL_splitstr, s - PL_splitstr);
return s;
And then the lexer generates the code
LINE: while (<>) {our #F=split(q\0,\0);
However this is of course compiled, and if you run it with B::Deparse you can see what is stored.
$ perl -MO=Deparse -F/e/ -e ''
LINE: while (defined($_ = <ARGV>)) {
our(#F) = split(/e/, $_, 0);
}
-e syntax OK
Being perl there is always a way, however ugly. (And this is some of the ugliest code I have written in a while):
use B::Deparse;
use Capture::Tiny qw/capture_stdout/;
BEGIN {
my $f_var;
}
unless ($f_var) {
$stdout = capture_stdout {
my $sub = B::Deparse::compile();
&{$sub}; # Have to capture stdout, since I won't bother to setup compile to return the text, instead of printing
};
my (undef, $split_line, undef) = split(/\n/, $stdout, 3);
($f_var) = $split_line =~ /our\(\#F\) = split\((.*)\, \$\_\, 0\);/;
print $f_var,"\n";
}
Output:
$ perl -Fe/\\\(\\[\\\<\\{\"e testy.pl
m#e/\(\[\<\{"e#
You could possible traverse the bytecode instead, since the start probably will be identical every time until you reach the pattern.

How to determine if shell command didn't run or produced no output

I am executing some shell commands via a perl script and capturing output, like this,
$commandOutput = `cat /path/to/file | grep "some text"`;
I also check if the command ran successfully or not like this,
if(!$commandOutput)
{
# command not run!
}
else
{
# further processing
}
This usually works and I get the output correctly. The problem is, in some cases, the command itself does not produce any output. For instance, sometimes the text I am trying to grep will not be present in the target file, so no output will be provided as a result. In this case, my script detects this as "command not run", while its not true.
What is the correct way to differentiate between these 2 cases in perl?
you can use this to know whether the command failed or the command return nothing
$val = `cat text.txt | grep -o '[0-9]*'`;
print "command failed" if (!$?);
print "empty string" if(! length($val) );
print "val = $val";
assume that text.txt contain "123ab" from which you want to get number only.
Use $? to check if the command executed successfully: see backticks do not return any value in perl for an example.
If you're not piping to |grep you can check $? for more specific exit status,
my $commandOutput = `grep "some text" /path/to/file`;
if ($? < 0)
{
# command not run!
}
elsif ($? >> 8 > 1)
{
# file not found
}
else
{
# further processing
}

How to append a string to next line in Perl

I have a requirement described in the following.
This just a sample script:
$ cat test.sh
#!/bin/bash
perl -e '
open(IN,"addrss");
open(out,">>addrss");
#newval;
while (<IN>)
{
#col_val=split(/:/);
if ($.==1)
{
for($i=0;$i<=$#col_val;$i++)
{
print("Enter value for $col_val[$i] : ");
chop($newval[$i]=<STDIN>);
}
$str=join(":");
$_="$str"
print OUT;
}
else
{
exit 0;
}
}
close(IN);
close(OUT);
Running this scipt:
$ ./test.sh
Enter value for NAME : abc
Enter value for ADDRESS : asff35
Enter value for STATE : XYZ
Enter value for CITY : EIDHFF
Enter value for CONTACT
: 234656758
$ cat addrss
NAME:ADDRESS:STATE:CITY:CONTACT
abc:asff35:XYZ:EIDHFF:234656758
When I ran it the second time:
$ cat addrss
NAME:ADDRESS:STATE:CITY:CONTACT
abc:asff35:XYZ:EIDHFF:234656758ioret:56fgdh:ghdgh:afdfg:987643221 ## it is appended in the same line...
I want it to be added to the next line.
NOTE:
I want to do this by explitly using the filehandles in Perl and not with redirection operators in shell.
I think you just need to add a newline to your string, before you print it out:
$_="$str\n";
Also, is there a reason why your script executes as bash, and you do a "perl -e" within the script? Why not just make the script file execute as perl?
I think if you add a newline to the end of the string you print, this will work:
$_="$str\n"
print OUT $_ . "\n";
or
instead of print, use printf and add a line feed to the front or back.