passing and calling multiple arguments in tcl - batch-file

I want to make a tcl procedure that can take N number of arguments. And I want to call it from a bat file.
This is the procedure I wrote:
proc multi_argu {args} {
foreach {path rev_start rev_end} $args {
puts "path-> $path"
puts "rev_start-> $rev_start"
puts "rev_end-> $rev_end"
}
}
multi_argu $argv
Now I call it from my bat file as
tclsh multi_argu.tcl 1 2 3
But the out is
path-> 1 2 3
rev_start->
rev_end->
At the end of my tcl file, I am calling multi_argu $argv which I think is the culprit. However, I do not know how to do this step for multiple arguments. can anyone provide any input?

Since $argv is a list, you are passing a list variable to your proc, which means $args becomes a list, containing one list element.
In tcl 8.5, you can make a minor change to the way you call the proc:
multi_argu {*}$argv
The {*} will enumerate the items of the list, so $args becomes a list of items.
Otherwise, I guess you could use something like:
foreach {path rev_start rev_end} [lindex $args 0] { ... }
This kind of defeats the purpose of using args as the arguments of the proc though. In that case, you could use another variable name too.

Do you have to define multi_argu as a proc? Your .bat file could just call multi_argu.tcl
that contains:
puts "Calling $argv0 with $argc arguments: $argv..."
foreach {path rev_start rev_end} $argv {
puts "path-> $path"
puts "rev_start-> $rev_start"
puts "rev_end-> $rev_end"
}
Now when you call it from your bat file as
tclsh multi_argu.tcl 1 2 3
The output is:
Calling multi_argu.tcl with 3 arguments: 1 2 3...
path-> 1
rev_start-> 2
rev_end-> 3

Related

Array in loop in TCSH shell

In TCSH :
I'm giving two lists containing different files in it. Then I have to pass those list as an array element and then the loop should execute twice as there are only 2 lists. But in this case loop is executing as much time as those files in both the lists.
set list_one = (one.s two.s three.s)
set list_two = (four.s five.s)
set arr=($list_one $list_two)
foreach i ($arr)
cat $i > $output.s
end
This is an example of my code, according to me loop should execute only twice(for list_one and list_two), but it's executing five times (for one.s two.s three.s four.s five.s)
If I use like this (mentioned below), loop executes for two times. But at cat command I get an error as, No such file or directory for each list that I passed in array.
set arr=(list_one list_two)
foreach i ($arr)
cat $i > $output.s
end
You were on the right track with your second approach, only a level of variable substitution was missing.
set arr=(list_one list_two)
foreach i ($arr)
eval cat \$$i >$output.s
end

Fill Two Arrays on the fly From stdin File in Bash

I wrote a bash script that reads a file from stdin $1, and needs to read that file line by line within a loop, and based on a condition statement in each iteration, each line tested from the file will feed into one of two new arrays lets say named GOOD array and BAD array. Lastly, I'll display the total elements of each array.
#!/bin/bash
for x in $(cat $1); do
#testing something on x
if [ $? -eq 0 ]; then
#add the current value of x into array called GOOD
else
#add the current value of x into array called BAD
fi
done
echo "Total GOOD elements: ${#GOOD[#]}"
echo "Total BAD elements: ${#BAD[#]}"
What changes should i make to accomplish it?
#!/usr/bin/env bash
# here, we're checking the number of lines more than 5 characters long
# replace with your real test
testMyLine() { (( ${#1} > 5 )); }
good=( ); bad=( )
while IFS= read -r line; do
if testMyLine "$line"; then
good+=( "$line" )
else
bad+=( "$line" )
fi
done <"$1"
echo "Read ${#good[#]} good and ${#bad[#]} bad lines"
Note:
We're using a while read loop to iterate over file contents. This doesn't need to read more than one line into memory at a time (so it won't run out of RAM even with really big files), and it doesn't have unwanted side effects like changing a line containing * to a list of files in the current directory.
We aren't using $?. if foo; then is a much better way to branch on the exit status of foo than foo; if [ $? = 0 ]; then -- in particular, this avoids depending on the value of $? not being changed between when you assign it and when you need it; and it marks foo as "checked", to avoid exiting via set -e or triggering an ERR trap when your boolean returns false.
The use of lower-case variable names is intentional. All-uppercase names are used for shell-builtin variables and names with special meaning to the operating system -- and since defining a regular shell variable overwrites any environment variable with the same name, this convention applies to both types. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html

Pass bash array to expect script

I have a bash script that calls an expect script like so
$SCRIPTS_DIRECTORY/my_expect_script.sh $my_bash_array
I can pass a variable over, it seems, and use it.
For this example the variable seems to be in [lindex $argv 0].
From bash, it will be a bunch of values, e.g. 1 2 3 4 5.
I am trying to figure out how to use expect to grab this variable, save it to an array then loop through the array to spit out multiple commands one at a time.
So my output should be something like
send command 1 \r
send command 2 \r
etc., until it reaches the end of the array.
I thought in expect I would assign it like
array set myArray [lindex $argv 0]
but it looks like I am wrong.
Would anyone have any good places I can look that might explain going from bash to expect better, or know how to do this? I assume its relatively simple, but expect is very iffy with me in some aspects.
Sample.sh
my_array=(1 2 3 4 5)
expect sample.exp "${my_array[#]}"
Sample.exp
foreach arg $argv {
puts "arg : $arg"
}
Output :
dinesh#mypc:~$ ./sample.sh
arg : 1
arg : 2
arg : 3
arg : 4
arg : 5
dinesh#mypc:~$
Expanding on #glenn jackman's comment...
Expect treats an Array as a set of key-pair values, while Expect treats a List simply as a list.
Therefore to be able to pass an array/list as a single arg...
Array Example (myExpectArray.tcl)
array set myArray [lindex $argv 0];
foreach item [array name myArray] {
puts $myArray($item)
}
Call the expect script from command line...
myExpectArray.tcl "0 dog 1 cat 2 rabbit"
To call this from bash, you'd need to loop through the array and build the string to pass to the expect script. Using a List is easier...
List Example (myExpectList.tcl)
set myList [lindex $argv 0];
for {set i 0} {$i < [llength $myList]} {incr i} {
puts [lindex $myList $i]
}
Call the expect script from bash as follows...
myAnimals+=( "dog" "cat" "rabbit" )
myArgs=${myAnimals[#]}
myExpectList.tcl "$myArgs"
Note: You must convert the list ($myAnimals) to a string ($myArgs) otherwise Expect treats each element of the array as a unique arg.

Passing an array directly to a function in Shell

is it possible to pass an array directly to a function in shell/bash, without saving it first to a variable?
currently example:
function checkParameter(){
param1=("${!1}")
for a in "${param1[#]}"; do
echo "${a}"
done
param2=("${!2}")
for b in "${param2[#]}"; do
echo "${b}"
done
}
a=( "child 1 from 1" "child 2 from 1" "child 3 from 1" )
b=( "child 1 from 2" "child 2 from 2")
checkParameter a[#] b[#]
So can I pass the ( "child 1 from 1" "child 2 from 1" "child 3 from 1" ) and the ( "child 1 from 2" "child 2 from 2") somehow directly to the function checkParameter, without saving it in a and b first?
greetings and thanks,
christopher2007
"$1" is immutable. That is why your example has to store the value of it in a temporary variable. This holds true for $# regardless of whether they are passed as an array or not.
Are you sure that you need to use an array ?
checkparameters() {
for param in "$#"; do
echo "$param"
done
}
checkparameters "a" "b" "c"
To answer the question, I don't think that is possible to pass an array as function's args without transformations.
EDIT
You could try something like :
[ ~]$ cat test.sh
#!/bin/bash
declare -a a1
a1=("a" "b")
declare -a a2
a2=("c" "d")
checkParameters(){
for i in "$#"; do
echo "Content of $i :"
for j in $(eval "echo \${$(echo $i)[#]}"); do
echo $j
done
done
}
checkParameters "a1" "a2"
[ ~]$ ./test.sh
Content of a1 :
a
b
Content of a2 :
c
d
Generally, you cannot pass arrays to Bash scripts or functions as arrays:
When you pass an array on the command line, e.g., "${ary[#]}", you're not passing an array, but its elements as individual parameters; the first array element is bound to special parameter $1, the second to $2, ...
Your solution attempt employs a clever workaround by passing the name of array variables to the function, where variable indirection (${!...}) is used to access the array inside the function.[1]
By definition, this approach requires the arrays to be stored in variables up front.
As for your desire to do it without storing your arrays in a variable: You cannot pass array literals as arguments in Bash; the conceptual equivalent of that is to simply pass individual parameters.
The problem with that is that you won't be able to tell where the elements of one array end and the next one begins.
You can address that by introducing a separator parameter whose sole purpose is to indicate when an array ends.
For instance, if you know that your elements never contain a newline, you can use $\n as the separator:
#!/usr/bin/env bash
function checkParameter(){
local -i arrayNdx=1
for param; do # Loop over all parameters
[[ $param == $'\n' ]] && { (( ++arrayNdx )); continue; }
echo "From array $arrayNdx: [$param]"
done
}
# Pass the array elements as individual parameters
# and use $'\n' (a newline) to separate the elements of the 1st
# array from those of the 2nd.
checkParameter "a1-child 1" "a1-child 2" "a1-child 3" $'\n' "a2-child 1" "a2-child 2"
[1] Just to show a simplified version of your solution to make the variable-indirection part clearer:
function checkParameter(){
for a in "${!1}"; do
echo "${a}"
done
for b in "${!2}"; do
echo "${b}"
done
}
checkParameter a[#] b[#]

Perl script can't match string in array despite me knowing a match is there

I have a Perl script that allows the user to enter a list of parameters as an argument with the command line using the -p flag. A subroutine in the script then loops through a longer set of parameters and checks if any of them match the parameters passed in with the -p flag.
Since it's possible to enter multiple parameters on the command line, the strings get stored in the variable $ARGV{'-p'} (I am using Getopt::Euclid). I convert this variable into an array and store it in a variable called #parlist. However, no matter what method I seem to use, I can never seem to get the script to recognize a match between a string (stored in a variable) and an element of the array, even when I print out the contents of both and see the exact matches myself.
My code:
#parameter being matched as an argument of the subroutine
my $pline = $_[0];
#parameter list as command line argument
my #parlist = #{$ARGV{'-p'}};
print "parlist: #parlist\n";
#extract parameter name from parameter line
$pline =~ /^.*?\._*?(.*)=.*$/;
my $pname = ($1);
print "pname: $pname\n";
#this doesn't find a match
my %p = map {$_ => 1 } #parlist;
if (exists $p{$pname}) {
print "Found it!\n";
}
#neither does this
my $match = grep { $_ eq $pname } #parlist;
print "$match\n";
#this doesn't either
if (grep { /$pname/ } #{$ARGV{'-p'}} ) {
print "Found it!\n";
}
Please note that the regex works fine. The two print lines at the top let me confirm when there is an actual match between the variable and the array, but the match checking still don't seem to work. The script is called like this:
./script -p streams_pool_size
Several loops through the subroutine might look something like this:
parlist: streams_pool_size
pname: java_pool_size
parlist: streams_pool_size
pname: streams_pool_size
parlist: streams_pool_size
pname: nls_language
The second set of results would indicate a match to me, or at least that is my perception of it.
Pulling my hair out trying to find what I'm doing wrong here. I'm sure the answer is right in from of me and I just haven't seen it yet.
Always use Data::Dumper when trying to print the contents of variables and arrays for testing purposes. Using print will not show invisible spaces in the strings that will cause mismatches. In the above code, the Dumper output was:
$VAR1 = {
'-parameter' => [
'streams_pool_size'
],
'-p' => $VAR1->{'-parameter'},
};
$VAR2 = 'streams_pool_size ';
The space at the end of $VAR2 was causing the mismatch.

Resources