Tcl print cwd and use it as string - tcl

I was making a small script in tcl for VMD:
mol load psf run_1/structure.psf xtc run_1/postDocking_wrapped.xtc
set final [atomselect top "not (water or ions or resid 1216)" frame last]
$final writepdb last_frame.pdb
puts "finished!"
quit
I wish i could add the folder I'm working in as a string to last_frame.pdb
like: last_frame_A1234.pdb where A1234 is my current folder.
When I try:
set path [file dirname [file normalize [info script] ] ]
it just returns a "."
I have also tried:
set path [file [info script]]
but the result is always the "." rather than A1234F.
If I put:
set path [pwd]
it would give me the full path (with the "/") and writepdb does not accept special characters.
Is there any way to just get the cwd? or from pwd access only to the last folder and use it as a string?
Thank you!

Try: set path [file tail [pwd]]

Related

Split Directory with dots and hyphen

I want to split this path so that I have the directory name as a variable. But he is interrupted.
path to splitt:
/home/user/T.A.T.E.-ano_ays-(ff-A-a)-ownage
code:
bind pub "-|-" !aaa pub:aaa
proc pub:aaa { nick uhost hand chan arg } {
set checkpath "/home/user/T.A.T.E.-ano_ays-(ff-A-a)-ownage"
set dirname [file rootname [file tail $checkpath]]
putnow "PRIVMSG $chan :dirname $dirname"
}
output:
dirname T.A.T.E
correct would be:
dirname T.A.T.E.-ano_ays-(ff-A-a)-ownage
How can fix the output from dirname
This is what file split was made for:
% lindex [file split /home/user/T.A.T.E.-ano_ays-(ff-A-a)-ownage] end
T.A.T.E.-ano_ays-(ff-A-a)-ownage
file tail without postprocessing via file rootname would also work:
% file tail /home/user/T.A.T.E.-ano_ays-(ff-A-a)-ownage
T.A.T.E.-ano_ays-(ff-A-a)-ownage
file rootname cuts the trailing filepath component at (not including) the last dot ..

How to get the path of one directory up from the current script path in tcl?

I wanted to get the path of one directory up in tcl as a string without using something like "cd .."
here is the following code i want to do
set script_path [ file dirname [ file normalize [ info script ] ] ]
puts $script_path
>>> example/foo
#add the needed code here
puts $script_path
>>> example
The file dirname command just lops one element off the end of the path each time, with a bit of trickiness to handle what happens when it runs out.
set path "example/foo/bar.tcl"; # You can get this from [info script] or whatever
puts $path; # ==> example/foo/bar.tcl
set path [file dirname $path]
puts $path; # ==> example/foo
set path [file dirname $path]
puts $path; # ==> example
set path [file dirname $path]
puts $path; # ==> .
Use it as many times as you need. (You might want to file normalize the path before you start doing this, which will convert the path into absolute form.)

Copy filename (with wildcard) in tcl

I'm trying to copy a file using a wildcard and it isn't being interpreted correctly.
set projName [lindex $argv 0]
puts "$projName chosen"
set sysdefPath "$projName/$projName.runs/impl_1/*.sysdef"
file copy -force $sysdefPath ./src/generatedFiles/$projName.hdf
I've tried a couple of variations of this but none have worked {*}, (*), [*], {.*}. The result of this places the wildcard (*) in the search path instead of trying to pattern match it.
What is the correct way to perform this?
Output,
$ test.tcl -tclargs proj
# set projName [lindex $argv 0]
# puts "$projName chosen"
proj chosen
# set sysdefPath "$projName/$projName.runs/impl_1/*.sysdef"
# file copy -force $sysdefPath ./src/generatedFiles/$projName.hdf
error copying "proj/proj.runs/impl_1/*.sysdef": no such file or directory
while executing
"file copy -force $sysdefPath ./src/generatedFiles/$projName.hdf"
(file "./src/projTcls/build_bitstream.tcl" line 5)
Your shell will expand file patterns wherever it finds them. Tcl is not like that: you have to explicitly ask for the list of files matching a pattern using the glob command: untested
set pattern $projName/$projName.runs/impl_1/*.sysdef
set sysdefPaths [glob -nocomplain -- $pattern]
switch -exact [llength $sysdefPaths] {
0 {error "No files match $pattern"}
1 {file copy -force [lindex $sysdefPaths 0] ./src/generatedFiles/$projName.hdf}
default {error "Multiple files match $pattern: [list $sysdefPaths]"}
}

TCL script to replace C file line

I have C file at
C:\SVN\Code\fileio.c
This reads 2 audio files as
tuningFile = fopen("../../simulation/micdata.bin", "rb");
mic1File = fopen("../../simulation/mic1.pcm", "rb");
I need to write TCL script code that will read the C file, and replace these 2 occurrences to
tuningFile = fopen("C:/SVN/simulation/micdata.bin", "rb");
mic1File = fopen("C:/SVN/simulation/mic1.pcm", "rb");
Can anyone give a compact example for something like below:
read file line wise
search for something like tuningFile = fopen(
extracting path from it and change it to absolute path
combine it with *tuningFile = fopen(
replace original line with modified line at same location
Thanks
sedy
The key is that you actually want to replace:
fopen("../../simulation/
with
fopen("C:/SVN/simulation/
That's easily done with string map. The rest of your problem is then just a matter of doing the file I/O, and pretty much any C source file that can be compiled by an ordinary compiler is best processed by loading it all into memory at once:
set filename {C:\SVN\Code\fileio.c}
set mapping [list {fopen("../../simulation/} {fopen("C:/SVN/simulation/}]
# Slurp the file in
set f [open $filename]
set data [read $f]
close $f
# Apply the mapping
set data [string map $mapping $data]
# Make the original a backup
file rename $filename $filename.bak
# Write back with a separate open
set f [open $filename w]
puts -nonewline $f $data
close $f
If you prefer, you can get the filename as an argument using, say, [lindex $argv 0]. The rest of the code doesn't care.
Here's a version that extracts the filename and uses file normalize on it:
set f [open $filename r]
set code [read $f]
close $f
set code [subst -novar -noback [regsub -all {((?:tuningFile|mic1File) = fopen\(")([^"]+)} $code {\1[file normalize "\2"]}]]
Breaking that up,
this command
regsub -all {((?:tuningFile|mic1File) = fopen\(")([^"]+)} $code {\1[file normalize "\2"]}
will find the string tuningFile = fopen("../relative/file (or "mic1file = ...") and replace it with the text
tuningFile = fopen("[file normalize "../relative/file"]
Then we feed that to subst so that embedded commands can be substituted, executing that file normalize command, resulting in the text
tuningFile = fopen("/full/path/to/file
Take 2: handle brackets in C code
$ pwd
/home/jackman/tmp/base/SVN/Code
$ tree ../..
../..
├── SVN
│   └── Code
│   ├── fileio.c
│   └── normalize.tcl
└── simulation
├── mic1.pcm
└── micdata.bin
3 directories, 4 files
$ cat fileio.c
int tuningFix[MAXTUNING];
tuningFile = fopen("../../simulation/micdata.bin", "rb");
mic1File = fopen("../../simulation/mic1.pcm", "rb");
$ cat normalize.tcl
#! tclsh
package require fileutil
set code [fileutil::cat [lindex $argv 0]]
# protect existing brackets
set bracketmap [list \[ \x01 \] \x02]
set code [string map $bracketmap $code]
# normalize filenames
set code [
subst -novar -noback [
regsub -all {((?:tuningFile|mic1File) = fopen\(")([^"]+)} $code {\1[file normalize "\2"]}
]
]
# restore brackets
set code [string map [lreverse $bracketmap] $code]
puts $code
$ tclsh normalize.tcl fileio.c
int tuningFix[MAXTUNING];
tuningFile = fopen("/home/jackman/tmp/base/simulation/micdata.bin", "rb");
mic1File = fopen("/home/jackman/tmp/base/simulation/mic1.pcm", "rb");
package require fileutil
set filename C:/SVN/Code/fileio.c
set mapping [list {fopen("../../simulation/} {fopen("C:/SVN/simulation/}]
proc replace {mapping data} {
string map $mapping $data
}
::fileutil::updateInPlace $filename [list replace $mapping]
Should work too. (Definition of mapping nicked from Donal.) updateInPlace calls the command prefix in its second argument, passes the contents of the file to that command, and updates the file with the result from the command.
This is very nearly the same procedure as in Donal's answer, expressed in higher-level code. If you want a backup copy, do this before calling updateInPlace:
file copy $filename [file rootname $filename].bak
Documentation: fileutil package, list, proc, set, string
based on great help from all users who commented, I was able to do the task as
proc replaceFileTemp {} {
global pth_fileio_orig
# create backup for copy back
set pth_backup [file rootname $pth_fileio_orig].bak
file copy $pth_fileio_orig $pth_backup
#get current file path
set thisFilePth [ dict get [ info frame [ info frame ] ] file ]
# get folder for current file
set thisFileFolderPth [file dirname $thisFilePth]
# set the replacement string/path
set replacementPth [file dirname $thisFileFolderPth]
# obtain original string to be replaced
set origPth "../../simulation/toplevel"
# download package for file manipulation
package require fileutil
set mapping [list $origPth $replacementPth]
proc replace {mapping data} {
string map $mapping $data
}
# replace original string with replacement string for all occurrences in file
::fileutil::updateInPlace $pth_fileio_orig [list replace $mapping]
}
# set the path to toplevel C file
set pth_fileio_orig [file normalize "../../../fileio.c"]
replaceFileTemp

copy files in one location to another and modify the copied file by placing some data at particular location in tcl?

i have to perform following operation..
copy file from one location to another
search a word in the given file
and move the file pointer to beginning of that line
place the data in that location which are copied from other file...
3 files are as follows:
C:\program Files(X86)\Route\*.tcl
C:\Sanity_Automation\Route\*.tcl
C:\Script.tcl
First i need to copy files from Route folder in Program Files to
Sanity_Automation\Route*.tcl
Then i need to search "CloseAllOutputFile keyword in
C:/Sanity_Automation/Route/SystemTest.tcl
once found, move cursor to the beginning of that line where "CloseAllOutputFile " keyword found.
and place data found on script.tcl to that location.
Firstly, that first "file" is actually a pattern. We need to expand that to a list of real filenames. We do that with glob.
# In braces because there are backslashes
set pattern {C:\Program Files(X86)\Route\*.tcl}
# De-fang the backslashes
set pattern [file normalize $pattern]
# Expand
set sourceFilenames [glob $pattern]
Then we want to copy them. We could do this with:
set target {C:\Sanity_Automation\Route\}
file copy {*}$sourceFilenames [file normalize $target]
But really we also want to build up a list of moved files so that we can process them in the next step. So we do this:
set target {C:\Sanity_Automation\Route\}
foreach f $sourceFilenames {
set t [file join $target [file tail $f]]
file copy $f $t
lappend targetFilenames $t
}
OK, now we're going to do the insertion processing. Let's start by getting the data to insert:
set f [open {C:\Script.tcl}]
set insertData [read $f]
close $f
Now, we want to go over each of the files, read them in, find where to do the insertion, actually do the insertion if we find the place, and then write the files back out. (You do text edits by read/modify-in-memory/write rather than trying to modify the file directly. Always.)
# Iterating over the filenames
foreach t $targetFilenames {
# Read in
set f [open $t]
set contents [read $f]
close $f
# Do the search (this is the easiest way!)
if {[regexp -indices -line {^.*CloseAllOutputFile} $contents where]} {
# Found it, so do the insert
set idx [lindex $where 0]
set before [string range $contents 0 [expr {$idx-1}]]
set after [string range $contents $idx end]
set contents $before$insertData$after
# We did the insert, so write back out
set f [open $t "w"]
puts -nonewline $f $contents
close $f
}
}
Normally, I'd do the modify as part of the copy, but we'll do it your way here.
Try this:
set sourceDir [file join / Files(x86) Route]
set destinationDir [file join / Sanity_Automation Route]
# Read the script to be inserted
set insertFnm [file join / Script.tcl]
set fil [open $insertFnm]
set insertData [read $fil]
close $fil
# Loop around all the Tcl scripts in the source directory
foreach inFnm [glob [file join $sourceDir *.tcl]] {
# Determine the name of the output file
set scriptName [file tail $inFnm]
set outFnm [file join $destinationDir $scriptName]
# Open source and destination files, for input and output respectively
set inFil [open $inFnm]
set outFil [open $outFnm w]
while {![eof $inFil]} {
set line [gets $inFil]
if {[string match *CloseAllOutputFile* $line]} {
puts $outFil $insertData
puts $outFil ""; # Ensure there's a newline at the end
# of the insertion
}
puts $outFil $line
}
# Close input and output files
close $inFil
close $outFil
}
It seems to work for me.