how to turn an array into a list - tcl

I'm trying to read each line in a file but it give an error every time I run it:
set fr [open temp.txt r]
set a [read $fr]
set b [split $a '\n']
foreach i $b{
*code*
}

This code works fine:
set fr [open input_file.txt r]
set a [read $fr]
close $fr
set b [split $a \n]
set fa [open temp.txt a]
foreach i $b {
#Process items in list b
puts $fa $i
}
close $fa

Related

how to compare lines of two files and change the matched line in one file in tcl

tcl
I wanna compare two files line by line.
file1
abc
123
a1b2c3
file2
abc
00 a1b2c3
if the line of file1 matched one of the line of file2, change the line of file1 to the line of file2
so the output file woule be like that.
file1
abc
123
00 a1b1c3
please help me
thank you
Here's a working example, if necessary adjust file paths to fit your needs.
This code makes a temporary work file that overwrites the original file1 at end.
set file1Fp [open file1 "r"]
set file1Data [read $file1Fp]
close $file1Fp
set file2Fp [open file2 "r"]
set file2Data [read $file2Fp]
close $file2Fp
set tempFp [open tempfile "w"]
foreach lineFile1 [split $file1Data "\n"] {
set foundFlag 0
foreach lineFile2 [split $file2Data "\n"] {
if { $lineFile1 == {} } continue
if { [string match "*$lineFile1*" $lineFile2] } {
set foundFlag 1
puts $tempFp "$lineFile2"
}
}
if { $foundFlag == 0 } {
puts $tempFp "$lineFile1"
}
}
close $tempFp
file rename -force tempfile file1
You could write
set fh [open file2]
set f2_lines [split [read -nonewline $fh] \n]
close $fh
set out_fh [file tempfile tmp]
set fh [open file1]
while {[gets $fh line] != -1} {
foreach f2_line $f2_lines {
if {[regexp $line $f2_line]} {
set line $f2_line
break
}
}
puts $out_fh $line
}
close $fh
close $out_fh
file rename -force $tmp file1
Depending on how you want to compare the two lines, the regexp command can also be expressed as
if {[string match "*$line*" $f2_line]}
if {[string first $line $f2_line] != -1}

tcl how to read files and show the certain words

I have a question few days ago ,but I think my expression is not clear and I separate my question into many small questions.
I have many files of process and it contain versions, I have regexp certain line of them and import them into a txt file , the txt format is like
#process #AA_version #BB_version
a11 Aa/10.10-d87_1 Bb/10.57-d21_1
a15 Aa/10.15-d37_1 Bb/10.57-d28_1
a23 Aa/10.20-d51_1 Bb/10.57-d29_3
and each process correspond its AA_version and BB_version
I want to write a tcl named get_tool_version.tcl to show /modify(not replace) the content
If I tclsh get_tool_version.tcl and input process and it will read the txt file and show it's
AA_version=Aa/
BB_version=Bb/
and then I can modify the string of AA and BB version
there is my code
set fp [open tool_version r+]
set file_data [read $fp]
close $fp
set data [split $file_data "\n"]
#input the process
set name [gets stdin] ->#and it'll show correspond AAand BB version
but I don't know how to show it's AA_version and BB_version
and how to modify them.
Or I need to use array?
thanks
Here's a way:
set fh [open tool_version r]
set data [dict create]
while {[gets $fh line] != -1} {
regexp {(\w+)\s+Aa/(\S+)\s+Bb/(\S+)} $line -> process aa bb
dict set data $process Aa $aa
dict set data $process Bb $bb
}
close $fh
set name a15 ;# you would get input from user here
puts "process = $name; Aa = [dict get $data $name Aa]; Bb = [dict get $data $name Bb]"
process = a15; Aa = 10.15-d37_1; Bb = 10.57-d28_1
The Tcl regex syntax is here: https://www.tcl-lang.org/man/tcl8.6/TclCmd/re_syntax.htm
here's my final version
set fp [open tool_version r]
set process [gets stdin]
while {[gets $fh line] != -1} {
if (regexp $process $line) {
dict set process1 Aa: [lindex $line 1]
dict set process1 Bb: [lindex $line 2]
puts "Aa: [lindex $line 1]"
puts "Bb: [lindex $line 2]"
}
}
close $fp
Thanks~

Compare columns between 2 files using TCL

I have 2 files having only one column. Say file1.txt and file2.txt.
Below are the contents inside the file
Inside file1.txt
Tom
Harry
Snowy
Edward
Inside file2.txt
Harry
Tom
Edward
2) I want to write a code that will check each item in the column and print something as below.
"Tom, Harry, Edward" are present in both the files
Snowy is there in file1.txt but not in file2.txt
3) Basic code
set a [open file1.txt r]
set b [open file2.txt r]
while {[gets $a line1] >= 0 && [gets $b line2] >= 0} {
foreach a_line $line1 {
foreach b_line $line2 {
if {$a_line == $b_line } {
puts "$a_line in file test1 is present in $b_line in file test2\n"
} else {
puts "$a_line is not there\n"
}
}
}
}
close $a
close $b
Issue is that it is not checking each name in the column.
Any suggestions.
Thanks in advance.
Neel
What you want to do is read each file separately and not have nested loops:
# read the contents of file1 into an associative array
# store the user as an array **key** for fast lookoup
set fh [open "file1.txt" r]
while {[gets $fh user] != -1} {
set f1tmp($user) ""
}
close $fh
# read file2 and compare against file1
array set users {both {} file1 {} file2 {}}
set fh [open "file2.txt" r]
while {[gets $fh user] != -1} {
if {[info exists f1tmp($user)]} {
lappend users(both) $user
unset f1tmp($user)
} else {
lappend users(file2) $user
}
}
close $fh
set users(file1) [array names f1tmp]
parray users
users(both) = Harry Tom Edward
users(file1) = Snowy
users(file2) =
Or as Donal suggests, use tcllib
package require struct::set
set fh [open file1.txt r]
set f1users [split [read -nonewline $fh] \n]
close $fh
set fh [open file2.txt r]
set f2users [split [read -nonewline $fh] \n]
close $fh
set results [struct::set intersect3 $f1users $f2users]
puts "in both: [join [lindex $results 0] ,]"
puts "f1 only: [join [lindex $results 1] ,]"
puts "f2 only: [join [lindex $results 2] ,]"
in both: Harry,Tom,Edward
f1 only: Snowy
f2 only:

Tcl echo file contents to transcript

i'm using some simulator that uses Tcl for transcript commands (Questa sim)
i want to echo file content like "cat" command in unix.
can it be done in one line command at tcl? is it possible to "cat" just the 5 first lines of file
In one line
puts [read [open data.dat r]]
OR step by step..
set handle [open data.dat r]
puts [read $handle]
close $handle
To open a file and echo its contents to standard output (just like cat), do this:
set f [open $filename]
fcopy $f stdout
close $f
To just do the first five lines (which is just like head -5), use this procedure:
proc head {filename {lineCount 5}} {
set f [open $filename]
for {set i 0} {$i < $lineCount} {incr i} {
if {[gets $f line] >= 0} {
puts $line
}
}
close $f
}
It takes more work because it's more complex to detect line endings than it is to just ship bytes around.
Here is the following code, to read 5 lines at a time from a given file.
#!/usr/bin/tclsh
set prev_count -1
set fp [open "input-file.txt" "r"]
set num_lines [split [read $fp] \n]
for {set i 4} {$i < [llength $num_lines]} { incr i 5} {
set line_5 [lrange $num_lines [incr prev_count] $i ]
set prev_count $i
puts "$line_5\n\n"
}

Insert lines of code in a file after n numbers of lines using tcl

I am trying to write a tcl script in which I need to insert some lines of code after finding a regular expression .
For instance , I need to insert more #define lines of codes after finding the last occurrence of #define in the present file.
Thanks !
When making edits to a text file, you read it in and operate on it in memory. Since you're dealing with lines of code in that text file, we want to represent the file's contents as a list of strings (each of which is the contents of a line). That then lets us use lsearch (with the -regexp option) to find the insertion location (which we'll do on the reversed list so we find the last instead of the first location) and we can do the insertion with linsert.
Overall, we get code a bit like this:
# Read lines of file (name in “filename” variable) into variable “lines”
set f [open $filename "r"]
set lines [split [read $f] "\n"]
close $f
# Find the insertion index in the reversed list
set idx [lsearch -regexp [lreverse $lines] "^#define "]
if {$idx < 0} {
error "did not find insertion point in $filename"
}
# Insert the lines (I'm assuming they're listed in the variable “linesToInsert”)
set lines [linsert $lines end-$idx {*}$linesToInsert]
# Write the lines back to the file
set f [open $filename "w"]
puts $f [join $lines "\n"]
close $f
Prior to Tcl 8.5, the style changes a little:
# Read lines of file (name in “filename” variable) into variable “lines”
set f [open $filename "r"]
set lines [split [read $f] "\n"]
close $f
# Find the insertion index in the reversed list
set indices [lsearch -all -regexp $lines "^#define "]
if {![llength $indices]} {
error "did not find insertion point in $filename"
}
set idx [expr {[lindex $indices end] + 1}]
# Insert the lines (I'm assuming they're listed in the variable “linesToInsert”)
set lines [eval [linsert $linesToInsert 0 linsert $lines $idx]]
### ALTERNATIVE
# set lines [eval [list linsert $lines $idx] $linesToInsert]
# Write the lines back to the file
set f [open $filename "w"]
puts $f [join $lines "\n"]
close $f
The searching for all the indices (and adding one to the last one) is reasonable enough, but the contortions for the insertion are pretty ugly. (Pre-8.4? Upgrade.)
Not exactly the answer to your question, but this is the type of task that lends towards shell scripting (even if my solution is a bit ugly).
tac inputfile | sed -n '/#define/,$p' | tac
echo "$yourlines"
tac inputfile | sed '/#define/Q' | tac
should work!
set filename content.txt
set fh [open $filename r]
set lines [read $fh]
close $fh
set line_con [split $lines "\n"]
set line_num {}
set i 0
foreach line $line_con {
if [regexp {^#define} $line] {
lappend line_num $i
incr i
}
}
if {[llength $line_num ] > 0 } {
linsert $line_con [lindex $line_num end] $line_insert
} else {
puts "no insert point"
}
set filename content_new.txt
set fh [open $filename w]
puts $fh file_con
close $fh