Expand passed arguments before printing with puts to virtual server - shell

I am having trouble with an expect script not evaluating arguments. Everything in the puts ${file_id} block (simplified obviously) gets placed onto a virtual machine and is later used to for configuration. The block you see puts the same code into a local directory for me to see if things are working properly.
global env
set env(os1) [lindex $argv 0]
set env(scratch_repo) /tmp/yum.repo_[pid]
set file_id [ open ${env(scratch_repo)} "w"]
puts ${file_id} {
root_image=node-${env(os1)}
if {[string first r ${env(os1)}] == 0} {
create_node_byid 1 [string range ${env(os1)} 0 4]-64
} else {
create_node_byid 1 [string range ${env(os1)} 0 5]-64
}
}
Unfortunately, the log file looks exactly as above. The arguments are not being substituted properly and I can't figure out why. I've tried using regular variables, different syntax for referencing local and global variables but have had no luck getting this to work. Any thoughts?

As Etan Reisner pointed use double quotes in the puts command instead of braces, so that it will get replaced.
puts ${file_id} "
root_image=node-${env(os1)}
if {[string first r ${env(os1)}] == 0} {
create_node_byid 1 [string range ${env(os1)} 0 4]-64
} else {
create_node_byid 1 [string range ${env(os1)} 0 5]-64
}
"
Assuming env(os1) as Ubuntu, will produce the following content in the file
root_image=node-Ubuntu
if {-1 == 0} {
create_node_byid 1 Ubunt-64
} else {
create_node_byid 1 Ubuntu-64
}
Note : This will only do variable substitutions not evaluation of code as such. i.e. if-else commands won't be evaluated.

Related

Why return value from a function following local declaration in bash is always 0?

In a script of bash below, I declare a variable followed by a function. And I tried to get the return value of the function. It should be 1 but it was always 0. When I remove the local declaration, it works as I want.
Why local declaration breaks the return value of the following function?
Is local declaration a type of command, like "if []"?
Please let me know the reason.
My test function is following,
function inner_func() {
local RETURN_00=1
echo "RETURN_00 = $RETURN_00"
echo "RETURN_00 = $RETURN_00" >&2
return $RETURN_00
}
function func() {
local ECHO_00=$(inner_func) # this local declaration is a problem
local RETURN_01=$?
echo "RETURN_01 = $RETURN_01"
echo "RETURN_01 = $RETURN_01" >&2
return $RETURN_01
}
ECHO_01=$(func)
RESULT=$?
echo "RESULT = $RESULT"
Results of above code are following
RETURN_00 = 1
RETURN_01 = 0
RESULT = 0
local is a command of its own accord, so using it resets $? to 0 (assuming the local assignment is successful), so when you do:
local ECHO_00=$(inner_func)
local RETURN_01=$? # $? has already been reset to 0 by the command local on the previous line.
Instead you can do this:
local ECHO_00
ECHO_00=$(inner_func)
local RETURN_01=$?
That way ECHO_00 is still local, but you can also access the return value from the function you called in the subshell. Output:
RETURN_00 = 1
RETURN_01 = 1
RESULT = 1
When a local variable is declared and assigned in the same command, the variable assignment takes place before the local declaration. So the value of $? is the return value of the local declaration operation.
See http://tldp.org/LDP/abs/html/localvar.html (3rd grey block from the top)

Parsing file, ignoring comments and blank lines

As the title says, I am trying to parse a file but ignore comments (started with #) or blank lines. I have tried to make a system for this, yet it always seems to ignore that it should be ignoring comments and/or blank lines.
lines := strings.Split(d, "\n")
var output map[string]bool = make(map[string]bool)
for _, line := range lines {
if strings.HasPrefix(line, "#") != true {
output[line] = true
} else if len(line) > 0 {
output[line] = true
}
}
When run (this is part of a function), it outputs the following
This is the input ('d' variable):
Minecraft
Zerg Rush
Pokemon
# Hello
This is the output when printed ('output' variable):
map[Minecraft:true Zerg Rush:true Pokemon:true :true # Hello:true]
My issue here is that it still keeps the "" and "# Hello" values, meaning that something failed, something I haven't been able to figure out.
So, what am I doing wrong that this keeps the improper values?
len(line) > 0 will be true for the "# Hello" line, so it will get added to output.
Currently, you are adding lines that either don't start with a # or are not empty. You need to only add lines that satisfy both conditions:
if !strings.HasPrefix(line, "#") && len(line) > 0 {
output[line] = true
}

Viewing enum names in vcs ucli

I am working in VCS UCLI (ie, the command line interface) and am having trouble getting VCS to display various state variables, of a typedef'd enum type, value as the name rather than the number. For example, I have some SystemVerilog like this:
typedef enum logic [1:0] {A, B, C} state_t;
state_t s;
...
Now in ucli, I want to see the value of s (say its in state A) so I type something like:
ucli% get s
0
ucli% get s -radix symbolic
0
ucli% show s -value
s 0
ucli% show s -value -radix symbolic
s 0
ucli% show s -value -type
s 0 { ENUM state_t { {A 0} {B 1} {C 2} } }
(Or something like that). I have read the ucli user guide, and it seems like symbolic radix, the only one I know of that might possibly be close, just uses the raw value from the enum, not the enum name. I have tried calling the .name() method for variable s using the call command in ucli (ucli% call {$display("%s", s.name())}), but it doesnt seem to be supported. I know VCS has the capacity to print the enum name, it certainly can in DVE, but I am having trouble coming up with ways to get to show me in the ucli.
Does anyone know how to get the ucli to print the enum name instead of the number when queried? Is enum-type radix somehow (user-defined like in DVE?), use some SystemVerilog system call to get the name, anything like that?
(Note, I understand I could just use the DVE, but I am trying to use the ucli to simply the interface for potential users, this is for educational purposes and I want to mask alot of the ucli interface (and VCS interface in general) to not overwhelm students and get some variables easily; Im turning the vcs ucli into a simple processor simulator)
++++++++++++ UPDATE ++++++++++++
I came up with a very hacky solution but I would really like a better approach. I quickly wrote my own wrapper for show (called eshow) whish ill replace any -value with the enum name if -radix enum is set:
#
# An extension of show to include "-radix enum"
#
# Borrowed from http://wiki.tcl.tk/17342
# Credit to Richard Suchenwirth (12-8-2006)
proc getopt {_argv name {_var ""} {default ""}} {
upvar 1 $_argv argv $_var var
set pos [lsearch -regexp $argv ^$name]
if {$pos>=0} {
set to $pos
if {$_var ne ""} {
set var [lindex $argv [incr to]]
}
set argv [lreplace $argv $pos $to]
return 1
} else {
if {[llength [info level 0]] == 5} {set var $default}
return 0
}
}
proc eshow {args} {
set argv $args
# If radix is not specified or value is not specified, then dont bother doing anything but regular show
if { 0 == [getopt argv -radix radix] } {
return [eval show $args]
}
if { 0 == [getopt argv -value] } {
return [eval show $args]
}
# If radix isnt enum, just pass off to regular show
if { 0 == [string equal -nocase $radix "enum"] } {
return [eval show $args]
}
# Now get the signal, its value and its type
set var [lindex [eval show $argv] 0]
set val [lindex [show $var -value] 1]
set typ [lindex [show $var -type] 1]
# If the type isnt an enum, error
if { 0 == [string equal -nocase [lindex $typ 0] "ENUM"] } {
return "The type of variable $var is not an enumerated type"
}
# Process the enumerations
set enuml [lindex $typ 2]
# Find the value name
foreach v $enuml {
if { $val == [lindex $v 1] } {
set enumname [lindex $v 0]
break
}
}
# If could not be found....
if { 0 == [info exists enumname] } {
return "The variabel $var has a value which does not map"
}
# Get rid of radix from args
getopt args -radix trashcan
# Replace all values with the name
set retval [eval show $args]
set retshow $retval
foreach v [lsearch -all $retval $val] {
set retshow [lreplace $retshow $v $v $enumname]
}
return $retshow
}
Thus, if I type any other non-radix enum eshow commands, it will pass to show, but otherwise, it will replace all values with thier names and return the same thing show would with the replacement. As I said, I REALLY want a better solution, but in case anyone wants to use my function, here it is.

Counting commands inside the proc in tcl?

Is there any way to count the commands before execution (may be callstack number including tclcmds) from specified proc name? I think it is needed to assume that the source is available (not for precomiled).
Thanking you.
Dynamic Analysis
You can use traces to find how many commands are executed during the execution of a particular procedure. Under the assumption that the command is not re-entered (i.e., isn't recursive) you do:
proc theProcedureOfInterest {} {
# Whatever in here...
for {set i 0} {$i < 5} {incr i} {
puts i=$i
}
}
trace add execution theProcedureOfInterest {enter leave enterstep} countCalls
proc countCalls {cmd args} {
global counter
switch [lindex $args end] {
enter {
set counter 0
}
enterstep {
incr counter
puts >>>$cmd
}
leave {
puts "$counter calls in $cmd"
}
}
}
theProcedureOfInterest
If you execute the above code, you get this output:
>>>for {set i 0} {$i < 5} {incr i} {
puts i=$i
}
>>>set i 0
>>>puts i=0
i=0
>>>incr i
>>>puts i=1
i=1
>>>incr i
>>>puts i=2
i=2
>>>incr i
>>>puts i=3
i=3
>>>incr i
>>>puts i=4
i=4
>>>incr i
12 calls in theProcedureOfInterest
That code has 12 command calls inside it, and you can count them yourself too.
This will also trace into procedures called from that procedure (and making it handle recursive calls is possible, but rather more involved). Be aware that changing the definition of the procedure will remove the trace (just reapply it if desired) and also note that this sort of tracing has a substantial performance impact (it greatly inhibits possible optimizations in Tcl's bytecode compiler).
Static Analysis
To get a static analysis of the code, you need the dkf-improved-disassembler branch (I've not merged it yet). Then, you can do:
set disassembled [::tcl::unsupported::getbytecode script {
# Whatever in here...
for {set i 0} {$i < 5} {incr i} {
puts i=$i
}
}]
set commandCount [llength [dict get $disassembled commands]]
You can also look at the commands element to see the identified commands (dict get [lindex [dict get $disassembled commands] $n] source). It will inspect into commands like for, but not into custom commands with bodies (since it doesn't understand that they're part of the code as opposed to just a funny string). It also has no idea how often they're executed; it's static analysis after all.

Difficulties understanding the mechanism of retrieving parameter values passed to a script in a function

The script passes two parameter values to another instance of the script. So the built-in parameter variable, 0, contains the number of passed parameters. 1 is in the below example "C:/Windows" and 2 is "/switchtest"
It is possible to assign the parameter values to strParam1 and strParam2 with the traditional method outside the function (with the single equal sign). However, inside a function, the assignments fail.
If they are assigned in a loop with the := sign, it seems to work.
Why is it? Can anybody explain this behavior?
strParam1 = %1%
strParam2 = %2%
msgbox, 64, Outside the Function, number of parameters:%0%`npath: %strParam1%`nswitch: %strParam2%
test_params()
strPath := "C:/Windows"
strSwitch := "/switchtest"
RunWait "%A_AhkPath%" "%A_ScriptFullPath%" "%strPath%" "%strSwitch%"
test_params() {
global 0
; this works
; loop %0%
; strParam%A_Index% := %A_Index%
; this causes an error: "This dynamic variable is blank. If this variable was not intended to be dynamic, remove the % symbols from it."
; strParam1 := %1%
; strParam2 := %2%
; this passes empty values; however, this method works outside the function.
strParam1 = %1%
strParam2 = %2%
msgbox, 64, Inside the Function, number of parameters:%0%`npath: %strParam1%`nswitch: %strParam2%
if strParam2
exitapp
}
You had the right idea with global 0; that allows %0% to carry into the function from toplevel. You just need to declare global 1, 2 as well.
Even if you do this, you can't use := to assign them to variables, because := deals with expressions and there is no syntax to use them in expressions (normally a variable is referred to in an expression with the variable name alone, without %%; obviously 1 and 2 are interpreted as actual numbers instead of variables).
#echristopherson answered the question, but I'd like to propose a workaround. This assumes you're using AutoHotkey_L.
If you run the test script with the args "a b c", it gives you this.
3
1, a
2, b
3, c
The test:
argv := args()
test := argv.MaxIndex() "`n"
for index,param in argv
test .= index ", " param "`n"
MsgBox % test
And the function:
args() {
global
local _tmp, _out
_out := []
Loop %0% {
_tmp := %A_Index%
if _tmp
_out.Insert(_tmp)
}
return _out
}

Resources