Unable to accept multiple commandline arguments into one variable within expect - scripting

Using cygwin 32bit, expect v5.45 (what comes with the latest cygwin, it seems).
COMMAND="$WIN_BUILD_ROOT\\scripts\\signBinaries.bat $BUS $NET_DRIVE $WIN_SIGNING_ROOT"
$CLIENT_BUILD_ROOT/scripts/runCommand.sh $arg1 $arg2 $arg3 $arg4 $COMMAND
runCommand.sh:
#!C:\cygwin\bin\expect.exe -f
set timeout 9
set arg1 [lindex $argv 0]
set arg2 [lindex $argv 1]
set arg3 [lindex $argv 2]
set arg4 [lindex $argv 3]
set COMMAND [lrange $argv 4 end]
send -- "$COMMAND\r"
Gives me:
{s:\git\builds\scripts\signBinaries.bat} 64 s {s:\git\builds\}
The filename, directory name, or volume label syntax is incorrect.
The scenario is, the first four arguments are fixed. There could then be a variable number of commands following those, which I want to have as one command to be executed by expect. If I just use [lindex argv 4], I only get the signBinaries script name, whether or not it's enclosed in quotes. Using lrange (found via googling) instead, it's enclosing the string arguments in braces, as shown. Why would it be modifying my arguments in this fashion and how do I fix it so that $COMMAND contains the command as I intended it to be?

In Tcl, which expect extends, you have to be aware of the datatype of your variables: is it a string or a list? When a list gets stringified, it may quote some of its elements if they contain "metacharacters" (like backslash and braces).
In most cases when you want to use the contents of a list as a single string, it's best to stringify it yourself:
set COMMAND [list "s:\\git\\builds\\scripts\\signBinaries.bat" 64 s "s:\\git\\builds\\"]
puts $COMMAND
# => {s:\git\builds\scripts\signBinaries.bat} 64 s s:\\git\\builds\\
puts [join $COMMAND " "]
# => s:\git\builds\scripts\signBinaries.bat 64 s s:\git\builds\

Aha! Figured it out:
set COMMAND [join [lrange $argv 4 end]]
Gives me:
s:\git\builds\scripts\signBinaries.bat 64 s s:\git\builds
"BUS: 64"
"DRIVE: s"
"WORKSPACE: s:\git\builds"

Related

sketchtool CLI with fish shell

I tried for a while to get the fish shell equivalent for the sketch cli initialization commands. Can anyone help?
For fish it the first line seems to work if you remove the '$' character. Second line for the argument passing I've tried removing the $, the quotes, & several different formats. Couldn't find documentation for argument passing initialization in fish.
#!/bin/sh
SKETCH=$(mdfind kMDItemCFBundleIdentifier == 'com.bohemiancoding.sketch3' | head -n 1)
# pass on all given arguments
"$SKETCH/Contents/Resources/sketchtool/bin/sketchtool" "$#"
reference: https://developer.sketch.com/cli/
Try:
set SKETCH (mdfind kMDItemCFBundleIdentifier == 'com.bohemiancoding.sketch3' | head -n 1)
$SKETCH/Contents/Resources/sketchtool/bin/sketchtool $argv

How to create sh script with expect?

I'm trying to create a sh script using expect script for embedded system (I don't want to change firmware to include this script). So, I have the following script, which doesn't work because of wrong usage of val in if-block:
#!/usr/bin/env expect
set timeout 20
set ipaddr [lindex $argv 0]
spawn telnet $ipaddr
expect "soc1 login: "
send "root\n"
expect "prompt # "
send "val=`some_command`\n"
expect "prompt # "
send "if [ \$val -eq 0 ]; then echo Good; fi\n"
# its here ^^^^^
expect "prompt # "
send "exit\n"
interact
I've tried to use $$ and it doesn't help.
How to fix this script to allow usage of variables inside sh script?
The problem of the script is in the interpretation by Tcl of [ and ] - these brackets should be escaped:
send "if \[ \$val -eq 0 \]; then echo Good; fi\n"
# its here ^^^^^

TCL/Expect variable vs $variable

Which one is proper way of using variable with or without dollar sign? I thought that variable (without $) is used only during variable declaration (similar to Bash):
set var 10
In all other cases when variable is referred or used (but not declared) the proper syntax is $variable (with $):
set newVar $var
puts $var
puts $newVar
But then I found code where it is interchanged and seems that this code is working:
# using argv
if {[array exists argv]} {
puts "argv IS ARRAY"
} else {
puts "argv IS NOT AN ARRAY"
}
# using $argv
if {[array exists $argv]} {
puts "\$argv IS ARRAY"
} else {
puts "\$argv IS NOT AN ARRAY"
}
# using argv
if {[string is list argv]} {
puts "argv IS LIST"
} else {
puts "argv IS NOT LIST"
}
# using $argv
if {[string is list $argv]} {
puts "\$argv IS LIST"
} else {
puts "\$argv IS NOT LIST"
}
Output:
argv IS NOT AN ARRAY
$argv IS NOT AN ARRAY
argv IS LIST
$argv IS LIST
Edit in reply to #glenn jackman:
Your reply pointed me to further research and I've found that TCL is capable doing some sort of "self modifying code" or whatever is correct name e.g.:
% set variableName "x"
x
% puts $x
can't read "x": no such variable
% set $variableName "abc"
abc
% puts $x
abc
% puts [set $variableName]
abc
%
%
%
%
%
%
% set x "def"
def
% puts $x
def
% puts [set $variableName]
def
%
Now your answer bring some light to problem, but one question remains. This is excerpt from documentation:
set varName ?value?
array exists arrayName
Documentation says that both functions expect variable name (not value) in other words it expect variable instead of $variable. So I assume (based on above self modifying code) that when I pass $variable instead of variable the variable substitution took place (exactly the same as code above). But what if $variable contains something that is not a list neither array (my arguments during testing was: param0 param1 "param 2" param3). From this point of view the output that says $argv IS LIST is wrong. What am I missing here?
Edit in reply to #schlenk:
Finally I (hope) understand the problematic. I've found great article about TCL, which explain (not just) this problematic. Let me pinpoint a few wise statement from this article:
In Tcl what a string represents is up to the command that's
manipulating it.
Everything is a command in Tcl - as you can see there is no
assignment operator.
if is a command, with two arguments.
The command name is not a special type but just a string.
Also following SO answer confirms this statement:
"In Tcl, values don't have a type... they question is whether they can be used as a given type."
The command string is integer $a means:
"Can I use the value in $a as an integer"
NOT
"Is the value in $a an integer"
"Every integer is also a valid list (of one element)... so it can be
used as either and both string is commands will return true (as will
several others for an integer)."
I believe the same applies also for string is list command:
% set abc "asdasd"
asdasd
% string is list $abc
1
% string is alnum $abc
1
string is list returns 1 because $abc is string and also it is one element list etc. In most tutorials there are said that following snippet is the proper way of declaring and working with lists:
% set list1 { 1 2 3 }
% lindex $list1 end-1
2
But when everything in TCL is string the following is also working in my experience (if I am wrong correct me please).
% set list2 "1 2 3"
1 2 3
% lindex $list2 end-1
2
It depends on the command. Some Tcl commands require a variable name as a parameter, if they need to modify the contents of the variable. Some are:
set
foreach
lappend
incr
Most but certainly not all commands want to take a variable's value.
You'll need to check the documentation for the relevant commands to see if the parameters include "varName" (or "dictionaryVariable"), or if the parameters are named as "string", "list", etc
An example using info exists which takes a varName argument:
% set argv {foo bar baz}
foo bar baz
% info exists argv ;# yes the variable "argv" exists
1
% info exists $argv ;# no variable named "foo bar baz"
0
% set {foo bar baz} "a value" ;# create a variable named "foo bar baz"
a value
% info exists $argv ;# so now that variable exists
1
The important thing to know is that $x in Tcl is just syntactical sugar for the command set x. So you can translate any $x in Tcl code into [set x] in the same place to see what really happens.
The other important thing to consider is immutable values. Tcl values are immutable, so you cannot change them. You can just create a new changed value. But you can change the value stored inside a variable.
This is where the difference between commands taking a variable name and those that take a value comes in. If a command wants to change the value stored in a variable, it takes a variable name. Examples are lappend, lset, append and so on. Other commands return a new value and take a value as argument, examples include lsort, lsearch, lindex.
Another important point is the fact that you do not really have a list type. You have strings that look like lists. So that is what Tcl's string is list tests. This has some consequences, e.g. you cannot always decide if you have a string literal or a one item list, as it is often the same. Example given:
% set maybe_list a
% string is list $maybe_list
1
Combine that with Tcls nearly unrestricted names for variables, as already demonstracted by Glenn and you can get really confused. For example, these are all valid Tcl variable names, you just cannot use all of them with the $ shortcut:
% set "" 1 ;# variable name is the empty string
1
% puts [set ""]
% set " " 1 ;# variable name is just whitespace
1
% puts [set " "]
1
% set {this is a list as variable name} 1 ;# a variable with a list name
1
% puts [set {this is a list as variable name}]
1
% set Δx 1
1
% incr Δx
2
% puts [set Δx]
2

Expect - Use default variable if no argument is specified

I've searched on stackoverflow and haven't really found an answer to this.
I'm pretty new to scripting and I have created a fully functional Expect script but I would like to improve it a bit. Currently I have created 3 lindex values of argv 0, 1 and 2 for hostaddress, username and password.
I would like to create a default username and password if argv 1 and 2 is NOT specified. I tried solving this through some if statements but after searching through stackoverflow it seems that TCL/Expect does not support NULL or empty values. Instead you have to make a query for it. Currently my code looks like this:
#!/usr/bin/expect
#Variables
set HOSTADDRESS [lindex $argv 0]
set USER [lindex $argv 1]
set PASSWORD [lindex $argv 2]
spawn ssh $USER#$HOSTADDRESS
set timeout 100
expect {
"(yes/no)?" {send "yes\n"; exp_continue}
"assword:" {send "$PASSWORD\n"}
}
expect {
"%" {send "cli\r"; exp_continue}
">" {sleep 1}
}
send "show interfaces st0 terse | match st0. | count \r"
expect "Count:???"
puts [open $HOSTADDRESS.op5.vpn.results w] $expect_out(0,string)
expect ">"
send "exit\r"
expect {
"%" {send "exit\r"; exp_continue}
"closed." {exit}
}
exit
Can you guys please help me create a default variable for $USER and $PASSWORD if it's not specified in the argv 1 or argv2?
Tcl doesn't support NULL at all. Or rather, it actually maps it to the variable being unset (that's exactly what happens with local variables under the hood; global variables are different). To query whether a variable exists, you use info exists (and yes, that's actually a NULL check in its implementation).
However, for handling defaulting of values from users on the command line, it is better to do it like this:
proc parseArgv {hostAddress {user "TheDefaultUser"} {pass "TheDefaultPassword"}} {
variable ::HOSTADDRESS $hostAddress
variable ::USER $user
variable ::PASSWORD $pass
}
parseArgv {*}$argv
If you're using 8.4 (upgrade, man!) then replace that last line with:
eval parseArgv $argv
You could also do it by looking at the llength of $argv (or the value in $argc) and doing conditional stuff based on that, but leveraging Tcl's proc default argument value stuff is easier (and you even get a reasonable error when someone gives too few or too many arguments).

using command line argument while using expect

Hi I am using expect to mput a file to a remote machine.I am passing the filename as a command line argument to the script.But error is throwing from the line
send -- "mput $1\r"
My code is as follows:
set timeout 1000
spawn ftp $ipaddress
expect "Name "
send -- "$username\r"
expect "Password"
send -- "$passwd\r"
expect "ftp>"
send -- "mput $1\r"//error thrown from this line
expect "mput $1? "
send -- "y\r"
expect "ftp>"
send -- "bye\r"
Could anyone please suggest what is wrong here?
Replace $1 with [lindex $argv 0]. The $argc variable tells the number of given arguments.