File Read and Write in tcl - tcl

I saw some previous posts related to opening a file to perform read and write operations but I din't get an answer for my task. I want to append some results to a file (should create a new file if it does not exist).
However, if the file already has result then append should be skipped and move on to next search for next result. I wrote a script for this and I have problem in reading the file.
The script is something like this:
proc example {} {
set a [result1 result2 ... result n]
set op [open "sample_file" "a+"]
set file_content ""
while { ![eof $op] } {
gets $op line
lappend file_content $line
}
foreach result $a {
if {[lsearch $file_content $result] == -1} {
puts $op $result
}
}
close $op
}
Note: In this script I find variable "line" to be empty {""}. I guess I have trouble in reading the file. Please help me with this

What you forgot, is to seek to the beginning of the file before reading:
proc example {} {
set a {result1 result2 ... result n}; # <== curly braces, not square
set op [open "sample_file" "a+"]
set file_content ""
seek $op 0; # <== need to do this because of a+ mode
while { ![eof $op] } {
gets $op line
lappend file_content $line
}
foreach result $a {
if {[lsearch $file_content $result] == -1} {
puts $op $result
}
}
close $op
}
Update
You can simplify the reading (while loop and all), with one single read statement:
proc example {} {
set a {result1 result2 result3}
set op [open "sample_file" "a+"]
seek $op 0
set file_content [read $op]
foreach result $a {
if {[lsearch $file_content $result] == -1} {
puts $op $result
}
}
close $op
}
example

Related

How to access .txt file using tcl PROC function

I have two files and I am comparing specific lines between two files using the def function.
def readPinFile(filename):
result = None
with open(filename, "r") as file:
result = {}
lastPin = None
for line in file:
lines = line.strip()
if lines[:3] == "PIN":
lastPin = lines.split(" ")[1]
result[lastPin] = {"LAYER": None, "RECT": None}
if lines[:5] == "LAYER":
result[lastPin]["LAYER"] = lines.split(" ")[1]
if lines[:4] == "RECT":
result[lastPin]["RECT"] = lines.split(" ")
return result
pin_of_file1 = readPinFile("osi_hbmp_top_briscm_1.lef") #lef file1
pin_of_file2 = readPinFile("osi_hbmp_top_briscm_2.lef")#lef file2
comparing between pins
with open("file04.txt", "r+") as output_file4: #compare same pins with layer and location
for pin, pin_data in pin_of_file1.items():
if pin in pin_of_file2:
if pin_of_file2[pin]["LAYER"] == pin_data["LAYER"] and pin_of_file2[pin]["RECT"] == pin_data["RECT"]:
output_file4.write(pin + "\n\n")
The TCL code I tried to get the same output
proc fileinput {filename} {
set filedata [open filename r]
set file1 [ read $filedata ]
foreach line [split $file1 \n] {
set pindata { PIN { LAYER {} RECT {} }}
if {[string match *PIN* $line]} {
dict lappend pindata PIN $line
}
if {[string match *LAYER* $line]} {
dict lappend pindata PIN {LAYER{$line}}
}
if {[string match *RECT* $line]} {
dict lappend pindata PIN {RECT{$line}}
}
}
return $pindata
}
set fileinput1 [fileinput osi_hbmp_top_briscm_1.txt]
set fileinput2 [fileinput osi_hbmp_top_briscm_2.txt]
In tcl I am trying to write comparing between the pins section, but I am stuck in the middle. i am fully confused to continue this code
foreach $pin, $pin_data [gets $fileinput1]
if{[string match $pin $fileinput2]}
This is the code I tried
The error trace tells you the immediate problem:
couldn't open "filename": no such file or directory
while executing
"open filename r"
(procedure "fileinput" line 2)
You need to give the name of the file, not the name of the variable containing the file name. Tcl cares about whether things are uses or references/names a lot. You fix this by using:
set filedata [open $filename r]
in the procedure; the added $ is vital as it says "read from the variable and use its value here".

Tcl How to sort certain words in the text and take the last one

I have a text and contains
#AA_VERSION = Aa/10.10-d87_1
#AA_VERSION = Aa/10.10-d887_1
#AA_VERSION = Aa/10.10-d138_1
#AA_VERSION = Aa/10.10-d82_1
How can I sort all the #AA_VERSION = beginning and print the last one?
And if the text don't have the # beginning ,how to show space or don't have version.
Thanks for your kindly help !!
Assuming you've already got a list of the contents of the lines, what you need to do is iterate over that list and test whether the line in question matches your critera; if it does, you store that matched information in a variable. At the end of the loop, the variable will contain the last such info that was matched.
set version ""
set current ""
foreach line $lines {
if {[regexp {^(#?)AA_VERSION *= *(.+)} $line -> commented info]} {
if {$commented eq "#"} {
set version [string trim $info]
} else {
if {$current ne ""} {
puts stderr "WARNING: multiple current versions"
}
set current [string trim $info]
}
}
}
# All lines scanned; describe what we've found
if {$version eq ""} {
puts "no #AA_VERSION line"
} else {
puts "#AA_VERSION is $version"
}
if {$current eq ""} {
puts "no current AA_VERSION"
} else {
puts "current AA_VERSION is $current"
}
The classic way to get a list of all lines in a file is this procedure:
proc linesOf {filename} {
set f [open $filename]
set data [read $filename]
close $f
return [split $data "\n"]
}
set lines [linesOf "mydata.txt"]

Unable to match string in a if loop in TCL

I am trying to check for a sentence in a file. I have this so far, but it always prints "one". Expected is "zero". Is there a problem with my regex?
File contents:
This is the header.
Test is a pass.
This is the footer.
Code is below:
set file [open "test.report" r]
while {[gets $file line] != -1} {
if {[regexp {\s+Test is a pass} $line]} {
puts "zero"
} else {
puts "one"
}
}
close $file
If you just want to check if a string is present, and you're not searching through too large a file (100MB isn't “too large” in this context, but 1GB is getting close) then you can just load the file in at once and use a line-aware regexp-matching mode.
set f [open "thefile.report"]
set data [read $f]
close $f
if {[regexp -line {\s+Test is a pass} $data]} {
puts "The test passed"
} else {
puts "The test did not pass"
}
You'll still need to think carefully about what to actually search for. In particular, if the file contains ANSI colour codes then your test needs to match them too.
For large files, processing a line at a time is right, but then you need to get the semantics right. It's easiest to write a helper procedure for this, and try…finally… helps a lot too:
proc isMatching {filename regular_expression} {
set f [open $filename]
try {
while {[gets $f line] >= 0} {
if {[regexp -- $regular_expression $line]} {
return true
}
}
return false
} finally {
close $f
}
}
if {[isMatching "thefile.report" {\s+Test is a pass}]} {
puts "The test passed"
} else {
puts "The test did not pass"
}
If you have files with single lines over 1GB long, then you've got something truly horrible. It's possible to build processing systems to cope with this, but it's nasty and requires trickier techniques. Ask again if you're unlucky enough to be stuck with this…

Tcl: How to replace variable string?

Input file is a tcl script and it looks like:
set PATH /user/abc/path
set PATH2 /user/abc/path2
...
read_verilog ${PATH}/src/vlog/code_1.v
read_verilog $PATH/src/vlog/code_2.v
read_vhdl ${PATH2}/src/vhd/code_3.vh
read_vhdl $PATH2/src/vhd/code_4.vh
[other commands ...]
Need to check if the source file is exist and print out none-exist files.
If none of the file is exist, the output looks like:
read_verilog ${PATH}/src/vlog/code_1.v
read_verilog $PATH/src/vlog/code_2.v
read_vhdl ${PATH2}/src/vhd/code_3.vh
read_vhdl $PATH2/src/vhd/code_4.vh
And below is my script:
#!/usr/bin/tclsh
set input_file "input.tcl"
set input_fpt [open $input_file r]
set input_lines_all [read $input_fpt]
set input_lines [split $input_lines_all "\n"]
set PATH /user/abc/PATH
set PATH /user/dgc/PATH2
foreach line $input_lines {
if { [string match "read_verilog *" $line] || [string match "read_vhdl*" $line] } {
regexp {[read_verilog read_vhdl] (.*)} $line matched file
if { [string match {*[{P]AT[H}]*} $file] } {
set abs_file [string map {${PATH} /user/abc/PATH} $file]
} elseif { [string match "*PATH2*" $file] } {
set abs_file [string map {${PATH2} /user/abc/PATH2} $file]
} else {
set abs_file $file
}
if { ![file exists $abs_file] } {
puts $line
}
}
}
My script can't check $PATH and not sure if there is a more efficient way to do the job.
The simplest way of doing just the substitutions you want is with the string map command. Build up the map piece by piece first, then apply it to your string.
set map {}
lappend map {$PATH} $PATH
lappend map {${PATH}} $PATH
lappend map {$PATH2} $PATH2
lappend map {${PATH2}} $PATH2
set modified [string map $map $inputString]
You can apply the map as many times as you want once you have built it, and transform your data either a line at a time or all in one go. However, you might be better off just evaluating the file as a Tcl script. That can be an incredibly useful approach to some types of parsing (especially when used in conjunction with a safe interpreter) if the input is suitable, which yours appears to be.

Variable in curly bracket double quote tcl

I am working this script I want script to replace the second line of my session.mvw file so I am asking input "name" if I enter 2222 as input I expect the second line of my session.mvw file as {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/222.rst"}
but instead its only giving
{GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}.
puts "Enter your name: "
#flush stdout set name [gets stdin]
set in [open session.mvw r]
# get the path to the parent directory
set hostDir [file dirname session.mvw]
set tempfile "$hostDir/temp2.txt"
# open/create a temp file
set out [open $tempfile w]
set count 0
while { [eof $in] != 1 } {
gets $in line
#set firstItem [lindex $line 0] incr count
# a match has been found...
if {$count == 2 } {
puts $out {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}
} elseif {$count == 3} {
puts $out {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}
} else {
puts $out $line
}
}
close $in
close $out
close $hostDir
# over-write the existing file
#file rename -force $tempfile session_file.mvw
Tcl's got a general rule that it doesn't do substitutions inside braces. That's usually exactly the right thing. However, in this case you need something a bit more. For these sorts of cases, there's the subst command which does do those substitutions:
puts $out [subst {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}]
(Note: If you're generating Tcl code with subst, you're probably doing it wrong. Not that this is what you're doing in this case, but still it's a warning to all readers…)
If you want to have braces around the value, you could do this
puts $out [list "GRAPHIC_FILE_1 = \"E:/ge work/hyperview scripting/${name}.rst\""]
or this
puts $out "{GRAPHIC_FILE_1 = \"E:/ge work/hyperview scripting/${name}.rst\"}"
both of which print the string
{GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/222.rst"}