I came across this scenario, where I have to execute tshark for decoding the pcap file.
Location of of tshark is C:\Program Files\Wireshark\tshark.exe
% set fileName TestTShark.pcap
TestTShark.pcap
% set sTsharkCmd "-r $fileName -Tfields -e ip.src"
-r TestTShark.pcap -Tfields -e ip.src
% set tsharkPath "C:/Program Files/Wireshark/tshark.exe"
C:/Program Files/Wireshark/tshark.exe
% eval exec $tsharkPath $sTsharkCmd > packet.log
couldn't execute "C:\Program": no such file or directory
% exec $tsharkPath $sTsharkCmd > packet.log
tshark: The file " TestTShark.pcap -Tfields -e ip.src" doesn't exist.
% set tsharkPath "C:\\Program Files\\Wireshark\\tshark.exe"
C:\Program Files\Wireshark\tshark.exe
% exec $tsharkPath $sTsharkCmd > packet.log
tshark: The file " TestTShark.pcap -Tfields -e ip.src" doesn't exist.
% eval exec $tsharkPath $sTsharkCmd > packet.log
couldn't execute "C:Program": no such file or directory
Since the path containing a space, while evaluating the code, Tcl treating C:\Program as a program name and throwing the error. Then I managed to escape the space with backslash as follows, which worked then.
% set tsharkPath "C:/Program\\ Files/Wireshark/tshark.exe"
C:/Program\ Files/Wireshark/tshark.exe
% eval exec $tsharkPath $sTsharkCmd > packet.log
% type packet.log
20.0.0.5
%
I am just curious. Is there any other option to handle this scenario ? Same problem observed with {*} also.
Your problem is that you're wanting space to be a part of an argument taken from variable, but to separate arguments coming from another argument. Tcl by default will try to apply the same uniform rules to all arguments: you need to do something extra to change that.
You want to use expansion of an argument. Your command
% eval exec $tsharkPath $sTsharkCmd > packet.log
Should be written as:
% exec $tsharkPath {*}$sTsharkCmd > packet.log
See how it is shorter? It's safer too. Try to avoid eval unless you really are doing something very complicated. (I hardly ever need it in my code…)
If you're stuck with an ancient version of Tcl without {*}, you need to do this instead:
% eval [linsert $sTsharkCmd 0 exec $tsharkPath > packet.log]
Which is horribly non-obvious, or this:
% eval {exec $tsharkPath} $sTsharkCmd {> packet.log}
Which isn't too nice either (and it's easy to forget to get it quoted right).
eval [list exec $tsharkPath {*}$sTsharkCmd]
Make the new command to evaluate with the sTsharkCmd attributes separated...
Related
I am trying to run a csh script within tcl script. The csh script contains bsub command in it. When i am trying to run script it is erroring out saying bsub: Command not found.
Script specimen :
exec cp -r x ../
cd ../../my_area
exec cp -r y ../../
cd ../../my_scripts
exec /bin/csh/ aim.csh >#stdout 2>#stderr
# also tried set hd aim.csh , exec /bin/csh $hd >#stdout 2>#stderr}
details of aim.csh
setenv IDC $d
setenv LGH $f
set error $e
bsub -q normal -db <path>
The correct way to invoke the script is like this:
exec /bin/csh aim.csh >#stdout 2>#stderr
You might want to make sure that the aim.csh file exists prior to running. It's important to do this after any cd call; that changes where the code will look for aim.csh.
If you have a problem with bsub not being on the executable search path, that's properly neither Tcl's nor csh's fault. (I've no idea where it would be, and can't remember which package supplies it.) You can extend the executable search path from your Tcl code if you need to:
# Before calling exec; after is too late
append env(PATH) :/path/to/directory
For example, if bsub were actually /opt/local/somewhere/bin/bsub, then you'd do:
append env(PATH) :/opt/local/somewhere/bin
A truly proper way to do this is with this procedure:
proc addToPath {directory} {
global env tcl_platform
if {[file type $directory] ne "directory"} {
error "must pass a directory name"
}
append env(PATH) $tcl_platform(pathSeparator) [file nativename $directory]
return
}
addToPath /opt/local/somewhere/bin
But the simple append is good enough for non-portable code.
When I try to execute below command it is showing Error like "extra characters after close-quote" but I gave it properly & when i try to it in unix command line terminal is opening properly.
exec gnome-terminal -e 'sh -c "bsub -Ip -n 1 -M <Memory> -q <queue_name> make"'
Can any one help me to resolve this issue or is there any way to do the same thing ??
Edited -> changed " from before sh to before bsub
Tcl's quoting is not the shell's quoting. Tcl uses {…} like the shell uses single quotes, except that braces nest nicely. Nesting single quotes is a recipe for shell headaches.
exec gnome-terminal -e {sh -c "bsub -Ip -n 1 -M <Memory> -q <queue_name> make"}
However, in this case I'd instead be tempted to go with this:
set memory "<Memory>"
set queue "<queue_name>"
set command "make"
set bsubcmd "bsub -Ip -n 1 -M $memory -q $queue $command"
# It's much more convenient to build this command like this here.
# Otherwise you're doing lots of backslashes and so on and it's horrible and very easy to make bugs
exec gnome-terminal -e [format {sh -c "%s"} $bsubcmd]
The only really messy thing is that command and bsubcmd have to be built using shell syntax if you're passing spaces around. “Fortunately” you're dealing with make anyway, so you probably really want to avoid having spaces in names passed there.
I run Modelsim in the cmd from a python program.
I use the following code which call a tcl script which run the modelsim:
os.system("vsim -c -do top_tb_simulate_reg.tcl " )
The tcl script contain the following:
vsim -voptargs="+acc" +UVM_TESTNAME=test_name +UVM_MAX_QUIT_COUNT=1 +UVM_VERBOSITY=UVM_LOW \
-t 1ps -L unisims_verm -L generic_baseblocks_v2_1_0 -L axi_infrastructure_v1_1_0 \
-L dds_compiler_v6_0_12 -lib xil_defaultlib xil_defaultlib.girobo2_tb_top \
xil_defaultlib.glbl
I want that the value of the +UVM_TESTNAME will be an argument which I passed from the cmd when I execute:
os.system("vsim -c -do top_tb_simulate_reg.tcl " )
How can I do it?
I tried the following with no succees:
Python script:
os.system("vsim -c -do top_tb_simulate_reg.tcl axi_rd_only_test" )
Simulation file (tcl script)
vsim -voptargs="+acc" +UVM_TESTNAME=$argv +UVM_MAX_QUIT_COUNT=1 +UVM_VERBOSITY=UVM_LOW \
-t 1ps -L unisims_verm -L generic_baseblocks_v2_1_0 -L axi_infrastructure_v1_1_0 \
-L dds_compiler_v6_0_12 -lib xil_defaultlib xil_defaultlib.girobo2_tb_top \
xil_defaultlib.glbl
I got the following error:
# ** Error: (vsim-3170) Could not find 'C:/raft/raftortwo/girobo2/ver/sim/work.axi_rd_only_test'.
The problem is that the vsim binary is doing its own processing of the arguments, and that is interfering. While yes, you can probably find a way around this by reading the vsim documentation, the simplest way around this is to pass values via environment variables. They're inherited by a process from its parent process, and are fine for passing most things. (The exception are security tokens, which should always be passed in files with correctly-set permissions, rather than either environment variables or command-line arguments.)
In your python code:
# Store the value in the *inheritable* environment
os.environ["MY_TEST_CASE"] = "axi_rd_only_test"
# Do the call; the environment gets passed over behind the scenes
os.system("vsim -c -do top_tb_simulate_reg.tcl " )
In your tcl code:
# Read out of the inherited environment
set name $env(MY_TEST_CASE)
# Use it! (Could do this as one line, but that's hard to read)
vsim -voptargs="+acc" +UVM_TESTNAME=$name +UVM_MAX_QUIT_COUNT=1 +UVM_VERBOSITY=UVM_LOW \
-t 1ps -L unisims_verm -L generic_baseblocks_v2_1_0 -L axi_infrastructure_v1_1_0 \
-L dds_compiler_v6_0_12 -lib xil_defaultlib xil_defaultlib.girobo2_tb_top \
xil_defaultlib.glbl
Late to the party but I found a great workaround for your obstacle. The do command within Modelsim's TCL instance does accept parameters. See command reference.
vsim -c -do filename.tcl can't take parameters, but you can use vsim -c -do "do filename.tcl params".
In your case this translates to os.system('vsim -c -do "do top_tb_simulate_reg.tcl axi_rd_only_test"'). Your .tcl script will find the parameter passed through the variable $1.
I hope to helps anyone!
I have a VB script which converts a csv file into xls. When this VB script is run in cmd prompt, it requires 2 arguments: path of csv file which is needed to be converted, and path of xls file where it is to be saved:
D:\csv2xlx.vbs D:\sample.csv D:\converted.xls
I want this to be executed using tcl by calling cmd prompt. When I write:
exec cmd.exe /c start D:\csv2xls.vbs D:\sample.csv D:\converted.xls
It gives an error. Can anybody suggest what is the problem?
The key problem you've probably got is that those backslashes are getting interpreted by Tcl instead of getting passed through. The fix is to use forward slashes and then process the filenames with file nativename during the exec:
exec cmd.exe /c start \
[file nativename D:/csv2xls.vbs] [file nativename D:/sample.csv] \
[file nativename D:/converted.xls]
Alternatively, use \\ instead of \ in those filenames. Or put them in braces.
exec cmd.exe /c start D:\\csv2xls.vbs D:\\sample.csv D:\\converted.xls
exec cmd.exe /c start {D:\csv2xls.vbs} {D:\sample.csv} {D:\converted.xls}
My machine no longer have VBScript on it, so I cannot test this out. Try:
exec cscript.exe D:\\csv2xls.vbs D:\\sample.csv D:\\converted.xls
Update: make double backslashes per Donal's suggestion.
In my TCL script I use rsync command and unfortunately it is refusing to sync path contain "*" character.
Code Example:
set srs "/users/home/username/common/*"
catch {exec rsync -av $src /tmp} res
puts $res
Output:
building file list ... done
sent 29 bytes received 20 bytes 98.00 bytes/sec
total size is 0 speedup is 0.00
rsync: link_stat "/users/home/username/common/*" failed: No such file or directory (2)
rsync error: some files could not be transferred (code 23) at main.c(977) [sender=2.6.9]
while executing
"exec rsync -rLptgov $src /tmp "
The canonical way of writing that is:
set srs "/users/home/username/common/*"
catch {exec rsync -av {*}[glob $src] /tmp} res
puts $res
This is because Tcl doesn't expand glob metacharacters by default; it's safer that way and easier to write correct code (it's hard to write good shell code that is resistant to problems in this area) but it does mean that you need to do a little extra work. The extra work is:
Ask for glob expansion of $srs with the glob command. That returns a list.
Ask for expansion of the list out of the glob by using the {*} pseudo-operator (it's not an operator — it's technically a kind of syntax — but it works a lot like one).
If you're using Tcl 8.4 (or before!) then you do it a bit different way:
set srs "/users/home/username/common/*"
catch {eval [list exec rsync -av] [glob $src] [list /tmp]} res
puts $res
OK, that's sometimes often shortened by leaving out the list bits (and there are many more obscure ways of writing it!) but that's a bad habit as it can cause huge problems when dealing with variables with values that aren't “nice”, e.g., with spaces in pathnames. If you have 8.5 or later, use {*}. Really.
To elaborate on what GrAnd said, you have to build the list of source files explicitly, probably using glob, so you'd do it like this
set cmd [concat [list exec rsync -av] \
[glob -nocomplain /users/home/username/common/*]]
lappend cmd /tmp
catch {eval $cmd} res
puts $res
or, alternatively, use {*} to expand the agument list for exec (this is for Tcl >= 8.5), like this:
set cmd [concat [list rsync -av] \
[glob -nocomplain /users/home/username/common/*]]
lappend cmd /tmp
catch {exec {*}$cmd} res
puts $res
Update: fixed example so that it really works.
That's because tcl interpreter does not expand the '*' symbol as shell does.
For example:
$ tclsh
% cd /usr
% exec ls -d *
ls: *: No such file or directory
% exec sh -c "ls -d *"
X11R6
bin
docs
etc
...