How to download and then use the file in the same tcl script? - tcl

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.

Related

How can I replace everything after a string using Bash?

I have a Perl script that uses some local variables as per below:
my $cool_variable="Initial value";
COOLVAR="Initial value for COOLVAR"
I would like to replace the content between the quotes using a bash script.
I got it to work for a non-variable like below:
#!/bin/sh
dummy_var="Replaced value"
sed -i -r "s#^(COOLVAR=).*#\1$dummy_var#" perlscript.pl
But if I replace it with cool_variable or $cool_variable:
sed -i -r "s#^($cool_variable=).*#\1$dummy_var#" perlscript.pl
It does not work..
The are multiple code injection bugs in that snippet. You shouldn't be generating code from the shell or sed.
Say you have
var=COOLVAR
val=coolval
As per How can I process options using Perl in -n or -p mode?, you can use any of
perl -spe's{^$var=\K.*}{"\Q$val\E";};' -- -var="$var" -val="$val" perlscript.pl
var=var val=val perl -pe's{^$ENV{var}=\K.*}{"\Q$ENV{val}\E";};' perlscript.pl
export var
export val
perl -pe's{^$ENV{var}=\K.*}{"\Q$ENV{val}\E";};' perlscript.pl
to transform
COOLVAR="dummy";
HOTVAR="dummy";
into
COOLVAR="coolvar";
HOTVAR="dummy";
The values are passed to the program using arguments to avoid injecting them into the fixer, and the fixer uses Perl's quotemeta (aka \Q..\E) to quote special characters.
Note that $var is assumed to be a valid identifier. No validation checks are performed. This program is absolutely unsafe using untrusted input.
Use -i to modify the file in place.

How to add text to the end of various digits using sed command?

I am trying to replace page=#" in various html files with page=#/index.html". I have tried using the command:
sed -i -re 's|"(page=[0-9]+)"|"\1/index.html"|' *.html
along with numerous interpretations but have not been successful. The first part of the code sed -i -re 's|"(page=[0-9]+)"| seems to be working properly but I cannot seem to format the end to achieve my goal. Any suggestions to modify this command would be greatly appreciated!
If you're trying to replace page=#" where the actual strings look like page=99", then the first double quote in the RE isn't going to match anything correctly. It would only match if it looks like:
"page=99"
But I'm guessing this is at the end of a link in html so it probably does not have the initial double quote. This should work instead:
`sed -i -re 's|(page=[0-9]+)"|\1/index.html"|' *.html
Also to confirm, if you're on OS X, you can't use the GNU option -r or use -i without an argument, so it would look like this:
`sed -i '' -Ee 's|(page=[0-9]+)"|\1/index.html"|' *.html
-E means to use Extended Regular Expressions so you can write ( instead of \( for grouping. In GNU sed this is -r.
-i means to edit the files in-place, on GNU it can take no argument, but on other systems you need to pass the extension to make for a backup, or '' for no backup.

SED in TCL/TK and any other equivalent command in TCL

I am trying pass value from TK to cshell script using "procedure call" now.... as follow.
proc Run {} {
global passedvalue
## to see what value it has for passedvalue
puts $passedvalue
exec sed -i {s/ABC/$passedvalue/g} runme.sh
exec /bin/csh -c ./runme.sh >#stdout 2>#stderr
}
I am changing a line which has value ABC by new passedvalue.
"puts" works and prints the value of passedvalue properly.
But it does not work for sed and it gives
Error : Program undefined variable
Please let me know how where I am doing wrong.
I have tried using string map as well but did work either...I might be doing something wrong.
Curly braces inhibit variable substitution. If you want $passedvalue to be expanded before calling exec, you'll need to use some other quoting mechanism.
For example, you could use double quotes:
exec sed -i "s/ABC/$passedvalue/g" runme.sh
You will need to add some extra bullet-proofing, however. For example, if $passedvalue Has a / in it, you will send a mal-formed expression to sed.

how to make a shell script function able to either specify arguments in command line or get them from a pipe?

For example, I want to write a function called fooFun, which will do some process on a PDF file. I'd like to make it able to run on both of the ways as following:
$ fooFun foo.pdf
$ ls *.pdf | fooFun
Any ideas? Thanks.
I don't think you can easily do this with a shell function. A better idea is to make it a script, let it take command line arguments, and achieve the second style with xargs:
ls *.pdf | xargs fooFun
I agree with #larsmans, better to stick with passing arguments as parameters. However, here's how to achieve what you're asking:
foofun() {
local args arg
if [[ $# -eq 0 ]]; then
args=()
# consume stdin
while IFS= read -r arg; do args+=($arg); done
else
args=("$#")
fi
# do something with "${args[#]}"
}

how to pass command line parameter containing '<' to 'exec'

$ date > '< abcd'
$ cat '< abcd'
<something>
$ tclsh8.5
% exec cat {< abcd}
couldn't read file " abcd": no such file or directory
whoops. This is due to the the specification of 'exec'.
If an arg (or pair of args) has one of the forms described below then it is used by exec to control the flow of input and output among the subprocess(es). Such arguments will not be passed to the subprocess(es). In forms such as “< fileName”, fileName may either be in a separate argument from “<” or in the same argument with no intervening space".
Is there a way to work around this?
Does the value have to be passed as an argument? If not, you can use something like this:
set strToPass "< foo"
exec someProgram << $strToPass
For filenames, you can (almost always) pass the fully qualified name instead. The fully qualified name can be obtained with file normalize:
exec someProgram [file normalize "< foo"] ;# Odd filename!
But if you need to pass in an argument where < (or >) is the first character, you're stuck. The exec command always consumes such arguments as redirections; unlike with the Unix shell, you can't just use quoting to work around it.
But you can use a helper program. Thus, on Unix you can do this:
exec /bin/sh -c "exec someProgram \"$strToPass\""
(The subprogram just replaces itself with what you want to run passing in the argument you really wanted. You might need to use string map or regsub to put backslashes in front of problematic metacharacters.)
On Windows, you have to write a batch file and run that, which has a lot of caveats and nasty side issues, especially for GUI applications.
One simple solution: ensure the word does not begin with the redirection character:
exec cat "./< abcd"
One slightly more complex:
exec sh -c {cat '< abcd'}
# also
set f {< abcd}
exec sh -c "cat '$f'"
This page on the Tcl Wiki talks about the issue a bit.
Have you tried this?
% exec {cat < abcd}
Try:
set myfile "< abcd"
exec cat $myfile