Shell script to build CLI args for a PERL script - sh

I have a Jenkins job, triggered as a parameterized build. It accepts an optional String parameter (HOSTNAMES) that can be a comma separated list of hostnames.
I need to pass this comma separated list of hostnames as a command line argument to a PERL script (within Execute shell build step).
Here is how I process the input parameter and construct the command line argument within the execute shell build step:
cmd_options=''
echo hostnames is $HOSTNAMES
if [ "$HOSTNAMES" != "" ]
then
cmd_options+=" --hostnames \"$HOSTNAMES\""
fi
perl myscript.pl $cmd_options
In the console output of the build though, I see the argument being passed incorrectly. Here is the console output:
+ cmd_options=
+ echo hostnames is host1, host2
hostnames is host1, host2
+ '[' 'host1, host2' '!=' '' ']'
+ cmd_options+=' --hostnames "host1, host2"'
+ perl myscript.pl --hostnames '"host1,' 'host2"'
I want myscript.pl to be called this way:
perl myscript.pl --hostnames "host1, host2"
I have tried various ways of manipulating $cmd_options using single quotes and double quotes, but have been unsuccessful so far in getting it right. Any pointers at where I am going wrong?

When you build a command, delay the interpolation and use eval to execute it.
HOSTNAMES='host1, host2'
cmd_options=''
if [ "$HOSTNAMES" != "" ]; then
cmd_options+='--hostnames "$HOSTNAMES"'
fi
eval "prog $cmd_options"
A better solution is to use an array.
HOSTNAMES='host1, host2'
cmd_options=()
if [ "$HOSTNAMES" != "" ]; then
cmd_options+=(--hostnames "$HOSTNAMES")
fi
prog "${cmd_options[#]}"
If prog is the following program:
#!/usr/bin/perl
use feature qw( say );
say 0+#ARGV; say for #ARGV
Both snippets output the following:
2
--hostnames
host1, host2

Looks like you will not be able to embed a list inside $cmd_options,
as it prevents you from using the quotation-marks properly -
"escaping" the quotation-marks with the backslash (\")
converts them to a regular " character - not a delimiter, and as such,
they are simply concatenated to the first and last items of the $HOSTNAMES list.
Suggest you drop this line:
cmd_options+=" --hostnames \"$HOSTNAMES\""
and, instead, use the following two lines, as needed
(this assumes you still need $cmd_options for passing other parameters)
perl myscript.pl $cmd_options --hostnames "$HOSTNAMES"
perl myscript.pl $cmd_options
Wrapped in an if statement, it should look like this:
if [ "$HOSTNAMES" != "" ]
then
perl myscript.pl $cmd_options --hostnames "$HOSTNAMES"
else
perl myscript.pl $cmd_options
fi
Another option is to make sure there are no spaces in the $HOSTNAMES list -
it will allow to pass the list as a single parameter and the quotation-marks will not be required anymore.

Assuming you don't need the script's positional parameters any more, you can set them yourself. (This will work in any POSIX shell, where arrays are unavailable.)
# Save any positional parameters first, if necessary;
# we're going to overwrite them.
first_arg=$1
second_arg=$2
# etc.
set -- --hostnames "$HOSTNAMES"
perl myscript.pl "$#"

Related

way to fetch the argument inside perl script

I am having some trouble of getting the argument passed in in the following script
echo "abc"|perl <<'EOF'
#how to get "abc". it seems not $ARGV[0] nor in <STDIN>
EOF
Thank you.
The precise command line you have there may be your problem, if that is what you're actually executing. What you are saying there is "put 'abc' on the standard input of the next thing in the pipeline. Now run a Perl script consisting of a single comment."
This will do nothing, because there's nothing executable in that Perl script. Try this:
echo "abc" | perl -e 'print <STDIN>'
If you have a short Perl script the -e option is the way to go.
Your example is not using argument, it's using standard input. You can read standard input with the I/O operators. If you actually mean that you want an argument like myscript.pl --arg then I would recommend using Getopt::Long.
You have not passed any argument to the Perl script.
You redirected the Perl script itself so it comes from standard input; that means that the piped output goes nowhere and cannot be seen by Perl.
Reconsider how you're invoking your script. Maybe:
perl script.pl "abc"
where script.pl is a file that contains the Perl script you used as a here-document. Or simply make that script executable (perhaps without the .pl suffix).
Your problem is that both the pipe and the here-document redirect the STDIN. And the here-document wins, so the perl process never sees the pipe; it gets the script on STDIN (and has read to EOF before running the script, so that will see STDIN at EOF).
Observe:
$ echo "abc" | perl <<'EOF'
print "[What have we here?]\n";
seek(STDIN, 0, 0);
print <STDIN>;
print "[Well, what do you know ...]\n";
EOF
[What have we here?]
print "[What have we here?]\n";
seek(STDIN, 0, 0);
print <STDIN>;
print "[Well, what do you know ...]\n";
[Well, what do you know ...]
$
Moral: Don't try to mix pipes and here-documents in the shell. :)

Run Perl Script From Unix Shell Script

Hi I have a Unix Shell Script call Food.sh ; I have a Perl Script call Track.pl. Is there a way where I can put Track.pl's code in to Food.sh code and still have it work ? Track.pl requires one arugement to label a name of a folder.
Basically it will run like this.
Unix Shell Script codes RUN
step into
Perl Script codes RUN
Put in name of folder for Perl script
Rest of script runs
exit out.
You have a few options.
You can pass the code to Perl using -e/-E.
...
perl -e'
print "Hello, World!\n";
'
...
Con: Escaping can be a pain.[1]
You can pass the code via STDIN.
...
perl <<'END_OF_PERL'
print "Hello, World!\n";
END_OF_PERL
...
Con: The script can't use STDIN.
You can create a virtual file.
...
perl <(
cat <<'END_OF_PERL'
print "Hello, World!\n";
END_OF_PERL
)
...
Con: Wordy.
You can take advantage of perl's -x option.
...
perl -x -- "$0"
...
exit
#!perl
print "Hello, World!\n";
Con: Can only have one snippet.
$0 is the path to the shell script being executed. It's passed to perl as the program to run. The -x tells Perl to start executing at the #!perl line rather than the first line.
Ref: perlrun
Instances of ' in the program needs to escaped using '\''.
You could also rewrite the program to avoid using '. For example, you could use double-quoted string literals instead of single-quoted string literals. Or replace the delimiter of single-quoted string literals (e.g. q{...} instead of '...'). As for single-quoted inside of double-quoted and regex literals, these can be replaced with \x27, which you might find nicer than '\''.
(I'm assuming your goal is just to have all of the code in a single file so that you don't have multiple files to install)
Sure, there's a way to do this, but it's cumbersome. You might want to consider converting the shell script entirely to Perl (or the Perl script entirely to shell).
So ... A way to do this might be:
#!/bin/sh
echo "shell"
perl -E '
say "perl with arg=$ARGV[0]"
' fred
echo "shell again"
Of course, you'd have to be careful with your quotes within the Perl part of the program.
You might also be able to use a heredoc for the Perl part to avoid quoting issues, but I'm not sure about that.

How to pipe Bash Shell command's output line by line to Perl for Regex processing?

I have some output data from some Bash Shell commands. The output is delimited line by line with "\n" or "\0". I would like to know that is there any way to pipe the output into Perl and process the data line by line within Perl (just like piping the output to awk, but in my case it is in the Perl context.). I suppose the command may be something like this :
Bash Shell command | perl -e 'some perl commands' | another Bash Shell command
Suppose I want to substitute all ":" character to "#" character in a "line by line" basis (not a global substitution, I may use a condition, e.g. odd or even line, to determine whether the current line should have the substitution or not.), then how could I achieve this.
See perlrun.
perl -lpe's/:/#/g' # assumes \n as input record separator
perl -0 -lpe's/:/#/g' # assumes \0 as input record separator
perl -lne'if (0 == $. % 2) { s/:/#/g; print; }' # modify and print even lines
Yes, Perl may appear at any place in a pipeline, just like awk.
The command line switch -p (if you want automatic printing) or -n (if you don't want it) will do what you want. The line contents are in $_ so:
perl -pe's/\./\#/g'
would be a solution. Generally, you want to read up on the '<>' (diamond) operator which is the way to go for non-oneliners.

Handling Perl command line arguments with spaces from a bash script?

This has been driving me nuts for hours now.
Consider the following test script in perl:
(hello.pl)
#!/usr/bin/perl
print "----------------------------------\n";
$numArgs = $#ARGV + 1;
print "thanks, you gave me $numArgs command-line arguments:\n";
foreach $argnum (0 .. $#ARGV) {
print "$ARGV[$argnum]\n";
}
Ok, it simply prints out the command line arguments given to the script.
For instance:
$ ./hello.pl apple pie
----------------------------------
thanks, you gave me 2 command-line arguments:
apple
pie
I can give the script a single argument with a space by surrounding the words with double quotes:
$ ./hello.pl "apple pie"
----------------------------------
thanks, you gave me 1 command-line arguments:
apple pie
Now I want to use this script in a shell script. I've set up the shell script like this:
#!/bin/bash
PARAM="apple pie"
COMMAND="./hello.pl \"$PARAM\""
echo "(command is $COMMAND)"
$COMMAND
I am calling the hello.pl with the same params and escaped quotes.
This script returns:
$ ./test.sh
(command is ./hello.pl "apple pie")
----------------------------------
thanks, you gave me 2 command-line arguments:
"apple
pie"
Even though the $COMMAND variable echoes the command exactly like the way I ran the perl script from the command line the second time, this time it does not want to see the apple pie as a single argument.
Why not?
This looks like the problem described in the Bash FAQ as: I'm trying to put a command in a variable, but the complex cases always fail!
The answer to that FAQ suggests a number of possible solutions - I hope that's of use.
The issue of the 2 command-line arguments
"apple
pie"
is due to shell expansion with the IFS shell variable being set to have a space as value.
printf '%q\n' "$IFS" # show value of IFS variable
You may use xargs & sh -c '...code...' to mimic / re-enable ordinary parameter parsing.
PARAM="'apple pie'"
printf '%s' "$PARAM" | xargs sh -c './hello.pl "$#"' argv0
Another option may be to write a few lines of C (like in shebang.c)!
http://www.semicomplete.com/blog/geekery/shebang-fix.html
You should try eval $COMMAND instead of simply $COMMAND.

replacing a variable in shell script using perl

I have a variable in a shell script,
var=1234_number
I want to replace all other than integer of $var .. how can I do it using a perl onliner?
You might be looking for something to edit the shell script, in which case, this might be sufficient:
perl -i.bak -e 's/\b(var=\d+).*/$1/' shellscript.sh
The '-i' overwrites the original file, saving a copy in shellscript.sh.bak; the substitute command finds assignments to 'var' (and not any longer name ending 'var') followed by an equals sign, some digits, and any non-digits, and leaves behind just the assignment of digits.
In the example, it gives:
var=1234
Note that the Perl regex is not foolproof - it will mangle this (dropping the closing brace).
: ${var=1234_number}
Dealing with all such possible variants is extremely fairly tricky:
echo $var=$other
OTOH, you might be looking to eliminate digits from a variable within a shell script, in which case:
var=$(echo $var | perl -e 's/\D//g')
You could also use 'sed' for the job:
var=$(echo $var | sed 's/[^0-9]//g')
No need to use anything but the shell for this
var=1234_abcd
var=${var%_*}
echo $var # => 1234
See 'Parameter Expansion' in the bash manual.