I have a c-shell script that hypothetically does a ton of things and gives little messages along the way.
Example (test.csh):
#!/bin/csh
echo "hello world."
sleep 10
echo "hello again."
sleep 10
I am calling the script from tclsh.
exec /bin/csh test.csh
All the output is held back until end of the script. That is not desired. Desired outcome is to get output from the script as it happens. How would I modify the TCL call to achieve the desired outcome?
Thanks.
One approach could be as follows:
exec >#stdout 2>#stderr /bin/csh test.csh
Check out the corresponding sections of the exec manpage, e.g.: >#
It turns out that your question has been raised before: How can I run a csh script from a tcl script?
Related
I'm not a Tcl programmer, but I need to modify a Tcl script that invokes an external command and tries to separate stdout and stderr. The following is a minimal example of how the script currently does this.
#!/usr/bin/tclsh8.4
set pipe [open "|cmd" r]
while {[gets $pipe line] >= 0} {puts $line}
catch "close $pipe" errorMsg
puts "$errorMsg"
Here, cmd is a an external command, and for the sake of this example, I will replace it with the following shell script. (I'm working on a Linux machine, but you can modify this to write to stdout and stderr however is appropriate for your system.)
#!/bin/sh -f
echo "A" > /dev/stdout
echo "B" > /dev/stdout
echo "C" > /dev/stderr
echo "D" > /dev/stderr
When I execute cmd, I get the following four lines as expected:
% ./cmd
A
B
C
D
However, when I execute my Tcl script, I get:
% ./test.tcl
A
B
D
This is an example of a more general phenomenon, which is that catch seems to swallow all but the last line of stderr.
To me, the "obvious" way to approach this is to try to mimic what is happening with stdout, which obviously works and prints all lines of the output. However, the current implementation is based on getting a Tcl channel by using open "|cmd", which requires running an external command. I can't figure out how to create a channel without opening an external command, and even if I could figure that out, there are subsequent issues with this approach. (How do I get the output of close into the channel? And if I need to open a new channel to get the output of each channel I am closing, then wouldn't I need an infinite number of channels?)
If anyone has any idea why errorMsg drops the initial lines or another approach that does not suffer from this problem, please let me know.
I know that this will come up, so I will say in advance that switching to Tcl 8.5 is probably not an option for me in the short term, since I do not control the environment in which this script is run.
I'm new using Tcl and I have the following script:
proc prepare_xml {pdb_id} {
set filename [exec wget ftp://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/$pdb_id.xml.gz]
set filename_unzip [exec gunzip "$pdb_id.xml.gz"]
set ready_xml [exec sed -i "/entry /c\<entry>" "$pdb_id.xml"]
return $ready_xml
}
The expected output is the file "filename" uncompress and modified. However, when I execute it the first time, it only downloads the file and it does not uncompress it. If I execute it for a second time, I obtained the expected output and a second copy of the original downloaded file.
Can anyone help me with this? I've tried with after and vwait commands but it doesn't work.
Thank you :)
It's hard to say for sure as you're not describing whether any errors are thrown (that'd be the only reason for the code to not run to completion), but I'd expect something like this to be the right approach:
proc prepare_xml {pdb_id} {
# Double quotes on next line just because of Stack Overflow highlighter
set url "ftp://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/$pdb_id.xml.gz"
set file $pdb_id.xml
append sedcode {/entry /} "c\\\n" {<entry>}
exec wget -q -O - $url | gunzip -c | sed $sedcode > $file
return $file
}
Firstly, I'm keeping complicated bits in (local) variables to stop the exec line from getting too long. Secondly, I've put all the subprocesses together in the one pipeline. Thirdly, I'm using -q and -O - with wget, and -c with gunzip; look up what they do if you don't understand them. Fourthly, I've put the scriptlet for sed in braces where possible to stop there from being trouble with backslashes, but I've used append and a non-backslashed section to make the pattern because the syntax of c in sed is downright weird (it needs a backslash-newline sequence immediately after on at least some platforms…)
I'd actually use native Tcl code to extract and transform the data if I was doing it for me, but that's a rather larger change.
I have a query that sends the results to an email. I would like not to send an email if the query has NO results. How can i do that ?
heres the code
mysql -umy -hmysql1.com -P2 -pmysq <<<" Select * from Data.data "| mail -aFrom:test#test.com -s 'test' test#gmail.com
Not every task can be done easily in a single command pipeline. Trying to force it into a one-liner can make it hard to code and hard to maintain.
Feel free to write some statements in a script:
result=`mysql -umy -hmysql1.com -P2 -pmysq -e " Select * from Data.data "`
if [ -n "$result" ]
then
echo "$result" | mail -aFrom:test#test.com -s 'test' test#gmail.com
fi
The -n test is for strings being nonzero length. Read http://linuxcommand.org/lc3_man_pages/testh.html for more details on that.
Re your comment:
The statements I showed above are things you could type at the command-line in bash. Bash supports variables and "if/then/else" constructs and a lot more.
Writing a bash script is easy. Anything you can type at the command-line can be in a file. Open a text editor and write the lines I showed above. Save the file. For example it could be called "mailmyquery.sh" (the .sh extension is only customary, it's not required).
Exit the text editor. Then run:
bash mailmyquery.sh
And it runs the statements in the file as if you had written them yourself at the command-line.
VoilĂ ! You are now a shell script programmer!
My TCL script:
source reboot_patch.tcl
set a 1
while {$a < 10} {
exec reboot_patch.tcl
after 60000
incr a
}
I need to run "reboot_patch.tcl" script for every 1 min in my system. I wrote above script. But its running only once and its coming out.
Following is the "reboot_patch.tcl" script:
#!/usr/bin/tcl
package require Expect
spawn telnet 40.1.1.2
expect "*console."
send "\r"
expect "*ogin:"
send "test\r"
expect "*word:"
send "test\r"
expect "*>"
send "clear log\r"
expect "*#"
send "commit \r"
expect "*#"
Please suggest me a way to achieve this.
Thanks in advance.
Script to print numbers from 1 to 10 in windows 7:
#!c:\Tcl\bin\tclsh
set a 1
while { $a < 11} {
puts $a
incr a
}
I am unable to run the above script using "./" format in windows7.
In general, exec command will return the output of program execution. It is our responsibility to capture and print and manipulate it.
You have to print it manually like
puts [ exec ./reboot_patch.tcl ]
Or like,
set result [ exec ./reboot_patch.tcl ]
puts $result
Since you are using exec without printing it's result, you have not seen anything. Then how come it got executed for the first time ? Who else can do except the following ?
source reboot_patch.tcl
Well, Since you have sourced the file and it got executed which seemed to be the first time execution but which is not actually from exec command.
Note : If you are calling any of that sourced file's proc, then only it is required to source it. As far as I can see you are not having any proc there. So, source is not required at all.
I am using an outside script from tcl. The script gives its result as a print out to stdout, so I use the command
set scriptRes [exec ${dir}/bin/script $obm_file]
$obm_file is an arguments for the script, the name of the input file for it.
In some cases the input file is not perfect, so the script will give good output and then will give an error, it prints the error message to stderr. Is there a way to tell tcl to take only the "good" output, i.e. the output that went to stdout, and disregard the error message?
The -ignorestderr option is what you need:
set scriptRes [exec -ignorestderr ${dir}/bin/script $obm_file]
Failing that (e.g., if your Tcl version is too old) use:
set scriptRes [exec ${dir}/bin/script $obm_file 2> /dev/null]