process multiple lines text file to print in single line - tcl

I am newbie in TCL scripting. Just wish to get some advice from experts here.
I wish to process the report with format as below. I am thinking to print the report without the header and with just single line like -
Connections for net 'pmg_ccu_ot2_tam_50mhz_sel_xainfwh' :: Driver a_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Output Pin (a_par) :: Load b_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Input Pin (b_par)
Connections for net 'pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2' :: Driver d_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2 Output Pin (d_par) :: Load e_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2 Input Pin (e_par), f_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_3 Input Pin (f_par)
<more . . .>
Really appreciate if anyone can give some light and idea to me. Thanks much!
****************************************
Report : net
-connections
Design : soc
Version: G-2012.06-SP2
Date : Sun Apr 7 22:56:33 2013
****************************************
Connections for net `pmg_ccu_ot2_tam_50mhz_sel_xainfwh`:
Driver Pins Type
------------ ----------------
a_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Output Pin (a_par)
Load Pins Type
------------ ----------------
b_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Input Pin (b_par)
1
Connections for net `pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2`:
Driver Pins Type
------------ ----------------
d_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2 Output Pin (d_par)
Load Pins Type
------------ ----------------
e_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_3 Input Pin (e_par)
f_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_3 Input Pin (f_par)
1
<more . . .>

If I'm understanding your requirements correctly, I would do something like this:
Input file contains:
****************************************
Report : net
-connections
Design : soc
Version: G-2012.06-SP2
Date : Sun Apr 7 22:56:33 2013
****************************************
Connections for net `pmg_ccu_ot2_tam_50mhz_sel_xainfwh`:
Driver Pins Type
------------ ----------------
a_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Output Pin (a_par)
Load Pins Type
------------ ----------------
b_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Input Pin (b_par)
1
Connections for net `pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2`:
Driver Pins Type
------------ ----------------
d_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2 Output Pin (d_par)
Load Pins Type
------------ ----------------
e_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_3 Input Pin (e_par)
f_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_3 Input Pin (f_par)
1
<more . . .>
The script:
# Read file containing input
set inputfile [open "inputfile.txt" r]
# File where output will be written in
set outputfile [open "outputfile.txt" w]
# $type will contain the Pin Type and $outputline will contain the final
# line to be written
set type ""
set outputline ""
# Read each line in the inputfile
while {[gets $inputfile line] != -1} {
# If first word in line is "Connections" execute
if {[lindex [split $line " "] 0] == "Connections"} {
# If $outputline not empty while in this if, it means that we have
# reached a new connection. So print out the current connection
# after joining all its elements by " :: " and reset the output line.
if {$outputline != ""} {
puts $outputfile [join $outputline " :: "]
set outputline ""
}
# Replacing the ` by ' and removing the end ":" and then putting
# the full thing into $output line as an element of a list
regsub -all {\`} $line "\'" newline
set newline [string trimright $newline "\:"]
lappend outputline $newline
}
# If there is "Pins" in the line, this means we have a header. The regexp
# captures the pin type by looking for any alphanumeric characters before
# "Pins"
regexp -- {([\w]+) Pins} $line - type
# If there is " Pin " in the line, it means we are in a row of the
# different 'Pins' type. Here, we put the while line after removing
# the extra white spaces into the $outputline list.
if {[regexp -- { Pin } $line]} {
lappend outputline "$type [string trim $line]"
}
}
# We reached the end of the document. So, add the last line into the outputfile.
puts $outputfile [join $outputline " :: "]
# Close the files we opened.
close $inputfile
close $outputfile
The output:
Connections for net 'pmg_ccu_ot2_tam_50mhz_sel_xainfwh' :: Driver a_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Output Pin (a_par) :: Load b_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh Input Pin (b_par)
Connections for net 'pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2' :: Driver d_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_2 Output Pin (d_par) :: Load e_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_3 Input Pin (e_par) :: Load f_par/pmg_ccu_ot2_tam_50mhz_sel_xainfwh_3 Input Pin (f_par)
This script will take 'Driver Type' or 'Load Type' in any order they come (and you can add new types as well). Are there any other possible variations in your in your input?
EDIT: You can change the switch block into this and it will take any type of Pins
if {[regexp -- {([\w]+) Pins} $line - type]} {
set counter 0
}
reEDIT: Added -- to the regexp.
rereEDIT: Matched edit in question.

Related

To check if specific string "PIN" present in the line or not -Tcl

The file contains a set of data I want to compare the "PIN" on each line if a string is present store the entire line into the new variable
PIN i_hbmc_ieee1500_sel_wir
DIRECTION INPUT ;
USE SIGNAL ;
PORT
LAYER K3 ;
RECT 2090.163000;
END
END i_hbmc_ieee1500_sel_wir
PIN i_hbmc_ieee1501_sel_wir
DIRECTION INPUT ;
USE SIGNAL ;
PORT
LAYER K3 ;
RECT 2090.163000;
END
END i_hbmc_ieee1500_sel_wir wir
The output is getting 1, The code I tried is here, and the expecting outcome as
PIN i_hbmc_ieee1500_sel_wir
PIN i_hbmc_ieee1501_sel_wir
set filedata [open "osi_hbmp_top_briscm_1.txt" r]
set file1 [ read $filedata ]
puts [string compare "PIN" $file1]
string compare (lexographically) compares if two strings are the same. see the docs. So 1 is returned because the input is lexographically greater then the "PIN".
If you want to print the lines starting with "PIN", you first need to split the input into lines (separating by the end-line character \n and then match the beginning:
foreach line [split $file1 \n] {
if {[string match "PIN*" $line]} {
puts $line
}
}
this will return
PIN i_hbmc_ieee1500_sel_wir
PIN i_hbmc_ieee1501_sel_wir

TCL Function throws an error

I have this function,
proc get_sys_status aname {
upvar $aname a
set input [exec "get system status\n" "# "]
set linelist [split $input \n]
foreach line $linelist
{
if {![regexp {([^:]+):(.*)} $line dummy key value]}
continue
switch -regexp -- $key
{
Hostname
{
set a(hostname) [string trim $value]
}
}
}
}
get_sys_status status
 
# line 17
puts "This machine is called $status(hostname)"
The general idea is that it takes the output of command “get system status” and parses the data. Once it parses this, it grabs the hostname and uses that to issue a few commands to the box. Error occurs at line 17 (the very last line):
can't read "status(hostname)": no such variable
----An error occured at line #17 of the script----
------- The end of log ----------
What is wrong with this function?

Need assitance with expect script

I am trying to achieve a certain expect script I have a txt file..
It contains data like
1 ip telnetname slot1 slot2 assoc1 assoc2 mep1 mep2
2 ip telnetname slot1 slot2 assoc1 assoc2 mep1 mep2
I want to make an expect script that for each line in the file it spawns a telnet session with variables set from the line like spawn telnet from line 1 with each 1 of those words as a variable set to be later used in commands.
This is what I tried so far
foreach ip $ips telnetname $telnetnames slot1 $slots1 slot2 $slots2 { commands here }
It isn't quite clear what you're trying to do from your description, but it is probably easiest to make your code work like this:
# Slurp the data into Tcl; it's not *that* long, is it?
set f [open "inputfile.txt"]
set data [read $f]
close $f
foreach line [split $data "\n"] {
# Tcl's [scan] is like C's sscanf(), but memory-safe!
if {[scan $line "%d %s %s %s %s" -> ip telnetname slot1 slot2] != 5} {
# Not what was expected; skip it!
continue
}
# Now do something with $ip, $telnetname, $slot1 and $slot2 ...
}

how to display the input in a desired format

input file contains some contents such as :
15-05-2011 16:05 <DIR> .
15-05-2011 16:05 <DIR> ..
24-04-2011 16:07 <DIR> Administrator
15-05-2011 16:05 <DIR> confuser
01-02-2011 20:57 <DIR> Public
29-01-2011 19:28 <DIR> TechM
12-08-2011 09:36 <DIR> vt0013487
I need to give the file name in the command line argument
the output to be in the desired format:
Administrator 24-04-2011 16:07
confuser 15-05-2011 16:05
Public 01-02-2011 20:57
TechM 29-01-2011 19:28
vt0013487 12-08-2011 09:36
So you're doing some fixed-width-field input parsing except that the final field is variable length and extends to the end of the line. That's easy enough. The only awkward bit is that we need to read in all the lines to get the width for the first field of the output format.
Assuming you supply the input on stdin (i.e., by redirection) and want it on stdout (so you can also redirect that to a file):
##### Read in and compute the width
set len 0
while {[gets stdin line] >= 0} {
set date [string range $line 0 16]
set name [string range $line 36 end]
lappend lines $name $date
if {[string length $name] > $len} {
set len [string length $name]
}
}
##### Write out as formatted
foreach {name date} $lines {
puts [format "%-*s %s" $len $name $date]
}
split to break the input into lines, foreach to iterate over them, regexp to extract relevant groups of characters from those lines, format to construct resulting strings (format is often not needed in Tcl as simple variable substitution in strings usually works just okay for common cases).
Read this, this, this and this. Also this for the syntax used by the regexp matching engine.
Also I suspect you may be trying to use the output generated by exec'ing the DOS dir command instead of using glob to traverse directories and files natively. If so, this is wrong, use glob
This may not answer your question: If you are calling cmd /c dir, here's a way to do it in Tcl:
package require struct::list
set files [glob *]
set maxlen [tcl::mathfunc::max {*}[struct::list map $files {apply {s {string length $s}}}]]
foreach file [lsort $files] {
set datetime [clock format [file mtime $file] -format {%d-%m-%Y %H:%M}]
puts [format {%-*s %s} $maxlen $file $datetime]
}

Parsing a file with Tcl

I have a file in here which has multiple set statements. However I want to extract the lines of my interest. Can the following code help
set in [open filename r]
seek $in 0 start
while{ [gets $in line ] != -1} {
regexp (line to be extracted)
}
Other solution:
Instead of using gets I prefer using read function to read the whole contents of the file and then process those line by line. So we are in complete control of operation on file by having it as list of lines
set fileName [lindex $argv 0]
catch {set fptr [open $fileName r]} ;
set contents [read -nonewline $fptr] ;#Read the file contents
close $fptr ;#Close the file since it has been read now
set splitCont [split $contents "\n"] ;#Split the files contents on new line
foreach ele $splitCont {
if {[regexp {^set +(\S+) +(.*)} $ele -> name value]} {
puts "The name \"$name\" maps to the value \"$value\""
}
}
How to run this code:
say above code is saved in test.tcl
Then
tclsh test.tcl FileName
FileName is full path of file unless the file is in the same directory where the program is.
First, you don't need to seek to the beginning straight after opening a file for reading; that's where it starts.
Second, the pattern for reading a file is this:
set f [open $filename]
while {[gets $f line] > -1} {
# Process lines
if {[regexp {^set +(\S+) +(.*)} $line -> name value]} {
puts "The name \"$name\" maps to the value \"$value\""
}
}
close $f
OK, that's a very simple RE in the middle there (and for more complicated files you'll need several) but that's the general pattern. Note that, as usual for Tcl, the space after the while command word is important, as is the space between the while expression and the while body. For specific help with what RE to use for particular types of input data, ask further questions here on Stack Overflow.
Yet another solution:
as it looks like the source is a TCL script, create a new safe interpreter using interp which only has the set command exposed (and any others you need), hide all other commands and replace unknown to just skip anything unrecognised. source the input in this interpreter
Here is yet another solution: use the file scanning feature of Tclx. Please look up Tclx for more info. I like this solution for that you can have several scanmatch blocks.
package require Tclx
# Open a file, skip error checking for simplicity
set inputFile [open sample.tcl r]
# Scan the file
set scanHandle [scancontext create]
scanmatch $scanHandle {^\s*set} {
lassign $matchInfo(line) setCmd varName varValue; # parse the line
puts "$varName = $varValue"
}
scanfile $scanHandle $inputFile
close $inputFile
Yet another solution: use the grep command from the fileutil package:
package require fileutil
puts [lindex $argv 0]
set matchedLines [fileutil::grep {^\s*set} [lindex $argv 0]]
foreach line $matchedLines {
# Each line is in format: filename:line, for example
# sample.tcl:set foo bar
set varName [lindex $line 1]
set varValue [lindex $line 2]
puts "$varName = $varValue"
}
I've read your comments so far, and if I understand you correctly your input data file has 6 (or 9, depending which comment) data fields per line, separated by spaces. You want to use a regexp to parse them into 6 (or 9) arrays or lists, one per data field.
If so, I'd try something like this (using lists):
set f [open $filename]
while {[gets $f line] > -1} {
# Process lines
if {[regexp {(\S+) (\S+) (\S+) (\S+) (\S+) (\S+)} $line -> name source drain gate bulk inst]} {
lappend nameL $name
lappend sourceL $source
lappend drainL $drain
lappend gateL $gate
lappend bulkL $bulk
lappend instL $inst
}
}
close $f
Now you should have a set of 6 lists, one per field, with one entry in the list for each item in your input file. To access the i-th name, for example, you grab $nameL[$i].
If (as I suspect) your main goal is to get the parameters of the device whose name is "foo", you'd use a structure like this:
set name "foo"
set i [lsearch $nameL $name]
if {$i != -1} {
set source $sourceL[$i]
} else {
puts "item $name not found."
set source ''
# or set to 0, or whatever "not found" marker you like
}
set File [ open $fileName r ]
while { [ gets $File line ] >= 0 } {
regex {(set) ([a-zA-Z0-0]+) (.*)} $line str1 str2 str3 str4
#str2 contains "set";
#str3 contains variable to be set;
#str4 contains the value to be set;
close $File
}