file name division and instantiation - foreach

set session_list [list ../../TIMING_ANALYSIS_CRPR/FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION ../../TIMING_ANALYSIS_CRPR/FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION ]
foreach j $session_list {
lappend x [regsub {^../../TIMING_ANALYSIS_CRPR/} $j ""]
}
foreach p $x {
lappend y [regsub {[^...]reports.15_03_10-22/SAVED_SESSION} $p ""]
}
foreach item $y {
lappend r "create_scenario -name $item"
}
foreach term $r {
lappend k " $term -image ./../ "
}
This is the code I created, the desired result is :
create_scenario -name FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax -image ./../FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION
create_scenario -name FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin -image ./../UNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION
How do I achieve this? My main problem is calling list"x" in the last foreach statement. Is there any alternative way?

You can (and I would even say should) use a single loop for that since you are doing the same thing for all elements of the list. From my understanding, you need to get the part after ../../TIMING_ANALYSIS_CRPR to get the name and the image is the name and everything after it.
So instead of removing what's not wanted, I would rather 'capture' what's wanted like this:
# Just another way to have a list
set session_list {
../../TIMING_ANALYSIS_CRPR/FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION
../../TIMING_ANALYSIS_CRPR/FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION
}
foreach item $session_list {
regexp -- {TIMING_ANALYSIS_CRPR/(([^/]+).+)} $item -> image name
set result "create_scenario -name $name -image ./../$image"
puts $result
}
# Prints:
# create_scenario -name FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax -image ./../FUNC_ss28_0.85V_0.95V_0.85V_125C_RCmax/reports.15_03_10-22/SAVED_SESSION
# create_scenario -name FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin -image ./../FUNC_ff28_0.85V_0.95V_0.85V_m40C_Cmin/reports.15_03_10-22/SAVED_SESSION
ideone demo
In the regexp, ([^/]+) will match and capture all characters except the forward slash character after TIMING_ANALYSIS_CRPR/. Then this, plus .+ (in (([^/]+).+) gets the name, and everything else after the name since . matches all characters.
Now, the regex can be a bit confusing since we are first getting the image then the name, but that's because the outer parentheses comes first, then the name is inside it.
If you don't want to (or you find regex too hard), you might try using file commands instead:
foreach item $session_list {
# Split on the directories
set elements [file split $item]
# Get the position of TIMING_ANALYSIS_CRPR
set id [lsearch $elements "TIMING_ANALYSIS_CRPR"]
# The name is just after it so...
set name [lindex $elements $id+1]
# The image is everything after the id:
set image [file join [lrange $elements $id+1 end]]
# Or if file join somehow is not using forward slashes:
# set image [join [list [lrange $elements $id+1 end]] "/"]
set result "create_scenario -name $name -image ./../$image"
}
Note: I am using Tcl8.6 if you are using Tcl8.4 or earlier versions, some of the syntax would have to be changed

Related

Join multiple lines into One (.cap file) CentOS

Single entry has multiple lines. Each entry is separated by two blank lines.
Each entry has to be made into a single line followed by a delimiter(;).
Sample Input:
Name:Sid
ID:123
Name:Jai
ID:234
Name:Arun
ID:12
Tried replacing the blank lines with cat test.cap | tr -s [:space:] ';'
Output:
Name:Sid;ID:123;Name:Jai;ID:234;Name:Arun;ID:12;
Expected Output:
Name:SidID:123;Name:JaiID:234;Name:ArunID:12;
Same is the case with Xargs.
I've used sed command as well but it only joined two lines into one. Where as I've 132 lines as one entry and 1000 such entries in one file.
You may use
cat file | awk 'BEGIN { FS = "\n"; RS = "\n\n"; ORS=";" } { gsub(/\n/, "", $0); print }' | sed 's/;;*$//' > output.file
Output:
Name:SidID:123;Name:JaiID:234;Name:ArunID:12
Notes:
FS = "\n" will set field separators to a newline`
RS = "\n\n" will set your record separators to double newline
gsub(/\n/, "", $0) will remove all newlines from a found record
sed 's/;;*$//' will remove the trailing ; added by awk
See the online demo
Could you please try following.
awk 'NF{val=(val?$0~/^ID/?val $0";":val $0:$0)} END{print val}' Input_file
Output will be as follows.
Name:SidID:123;Name:JaiID:234;Name:ArunID:12;
Explanation: Adding explanation of above code too now.
awk ' ##Starting awk program here.
NF{ ##Checking condition if a LINE is NOT NULL and having some value in it.
val=(val?$0~/^ID/?val $0";":val $0:$0) ##Creating a variable val here whose value is concatenating its own value along with check if a line starts with string ID then add a semi colon at last else no need to add it then.
}
END{ ##Starting END section of awk here.
print val ##Printing value of variable val here.
}
' Input_file ##Mentioning Input_file name here.
This might work for you (GNU sed):
sed -r '/./{N;s/\n//;H};$!d;x;s/.//;s/\n|$/;/g' file
If it is not a blank line, append the following line and remove the newline between them. Append the result to the hold space and if it is not the end of the file, delete the current line. At the end of the file, swap to the hold space, remove the first character (which will be a newline) and then replace all newlines (append an extra semi-colon for the last line only) with semi-colons.

PowerShell Parse INF file

I am trying to parse an INF; specifically, driver version from the file. I am new to PowerShell, so I've gotten only this far.
The file looks like this:
[Version]
Signature = "$WINDOWS NT$"
Class = Bluetooth
ClassGuid = {e0cbf06c-cd8b-4647-bb8a-263b43f0f974}
Provider = %PROVIDER_NAME%
CatalogFile = ibtusb.cat
DriverVer=11/04/2014,17.1.1440.02
CatalogFile=ibtusb.cat
The second last line has the information I am looking for. I am trying to parse out just 17.1.1440.02.
One file may contain multiple lines with DriverVer=..., but I am only interested in the first instance.
Right now I've the following script.
$path = "C:\FilePath\file.inf"
$driverVersoin = Select-String -Pattern "DriverVer" -path $path
$driverVersoin[0] # lists only first instance of 'DriverVer'
$driverVersoin # lists all of the instances with 'DriverVer'
Output is:
Filepath\file.inf:7:DriverVer=11/04/2014,17.1.1440.02
But I am only looking for 17.1.1440.02
Make your expression more specific and make the part you want to extract a capturing group.
$pattern = 'DriverVer\s*=\s*(?:\d+/\d+/\d+,)?(.*)'
Select-String -Pattern $pattern -Path $path |
select -Expand Matches -First 1 |
% { $_.Groups[1].Value }
Regular expression breakdown:
DriverVer\s*=\s* matches the string "DriverVer" followed by any amount of whitespace, an equals sign and again any amount of whitespace.
(?:\d+/\d+/\d+,)? matches an optional date followed by a comma in a non-capturing group ((?:...)).
(.*) matches the rest of the line, i.e. the version number you want to extract. The parentheses without the ?: make it a capturing group.
Another option (if the version number is always preceded by a date) would be to just split the line at the comma and select the last field (index -1):
Get-Content $path |
Where-Object { $_ -like 'DriverVer*' } |
Select-Object -First 1 |
ForEach-Object { $_.Split(',')[-1] }

Variable range operator in PowerShell

I am trying to use a range operator to input a series of numbers for use in a PowerShell script. Here is my code:
$computers = servername + [1-9]
I would like the $computers variable to iterate the 1-9 i.e., servername1, servername 2, etc. etc. Any ideas?
1..9 | % { $computers += "servername$_`n" }
And the variable $computers will contain:
servername1
servername2
servername3
[...]
Try running only the 1..9 part on your command line and it'll be easier to see what's gonig on. You could also read up on arrays in PowerShell with Get-Help about_Arrays - look for the part about "range operator" near the beginning.
The following line of code does the same thing (and seems cleaner to me) and might be easier to understand as well.
$computers = 1..9 | foreach { "servername$_" }
Or simply 1..9 | foreach { "servername$_" } to see it on screen without saving it in a variable.

Read Text File with Names Containing Brackets?

I find something interesting when I try to read text files from a folder:
$files = Get-ChildrenItem "C:\MyFolder" -Recursive | ?{$_.Extension -like '.txt'}
foreach ($file in $files) {
[string]fileFullName = $file.FullName
$content = Get-Content -Path $fileName
....
}
I encountered some exceptions if file names contains [ and ] chars, for example, a[1].txt.
I guess that every thing is object in PS. Therefore, as for my understanding, [..] in file names maybe treated as index access. Is there any way to deal with files with brackets?
You want -LiteralPath:
$files = Get-ChildItem "C:\MyFolder" -Recurse | ? { $_.Extension -like '.txt' }
foreach ($file in $files) {
[string]$fileFullName = $file.FullName
$content = Get-Content -LiteralPath $fileFullName
$content
}
From detailed help () you willsee:
-LiteralPath
Specifies the path to an item. Unlike Path, the value of LiteralPath is used exactly as it is typed. No characters are
interpreted as wildcards. If the path includes escape characters,
enclose it in single quotation marks. Single quotation marks tell
Windows PowerShell not to interpret any characters as escape
sequences.

Powershell parse parts of a text file and save to CSV

All, I'm very new to powershell and am hoping someone can get me going on what I think would be a simple script.
I need to parse a text file, capture certain lines from it, and save those lines as a csv file.
For example, each alert is in its own text file. Each file is similar to this:
--start of file ---
Name John Smith
Dept Accounting
Codes bas-2349,cav-3928,deg-3942
iye-2830,tel-3890
Urls hxxp://blah.com
hxxp://foo.com, hxxp://foo2.com
Some text I dont care about
More text i dont care about
Comments
---------
"here is a multi line
comment I need
to capture"
Some text I dont care about
More text i dont care about
Date 3/12/2013
---END of file---
For each text file if I wanted to write only Name, Codes, and Urls to a CSV file. Could someone help me get going on this?
I'm more a PERL guy so I know I could write a regex for capturing a single line beginning with Name. However I am completely lost on how I could read the "Codes" line when it might be one line or it might be X lines long until I run into the Urls field.
Any help would be greatly appreciated!
Text parsing usually means regex. With regex, sometimes you need anchors to know when to stop a match and that can make you care about text you otherwise wouldn't. If you can specify that first line of "Some text I don't care about" you can use that to "anchor" your match of the URLs so you know when to stop matching.
$regex = #'
(?ms)Name (.+)?
Dept .+?
Codes (.+)?
Urls (.+)?
Some text I dont care about.+
Comments
---------
(.+)?
Some text I dont care about
'#
$file = 'c:\somedir\somefile.txt'
[IO.File]::ReadAllText($file) -match $regex
if ([IO.File]::ReadAllText($file) -match $regex)
{
$Name = $matches[1]
$Codes = $matches[2] -replace '\s+',','
$Urls = $matches[3] -replace '\s+',','
$comment = $matches[4] -replace '\s+',' '
}
$Name
$Codes
$Urls
$comment
If the file is not too big to be processed in memory, the simple way is to read it as an array of strings. (What too big means is subject to your system. Anything sub-gigabyte should work without too much a hickup.)
After you've read the file, set up a head and tail counters to point to element zero. Move the tail pointer row by row forward, until you find the date row. You can match data with regexps. Now you know the start and end of a single record. For the next record, set head counter to tail+1, tail to tail+2 and start scanning rows again. Lather, rinse, repeat until end of array is reached.
When a record is matched, you can extract name with a regex. Codes and Urls are a bit trickier. Match the Codes row with a regex. Extract it and all the next rows unless they do not match the code pattern. Same goes to Urls data. If the file always has whitespace padding on rows that are data to previous Urls and Codes, you could use match whitespace count with a regexp to get data rows too.
Maybe something line this would to it:
foreach ($Line in gc file.txt) {
switch -regex ($Line) {
'^(Name|Dept|Codes|Urls)' {
$Capture = $true
break
}
'^[A-Za-z0-9_-]+' {
$Capture = $false
break
}
}
if ($Capture) {
$Line
}
}
If you want the end result as a CSV file then you may use the Export-Csv cmdlet.
According the fact that c:\temp\file.txt contains :
Name John Smith
Dept Accounting
Codes bas-2349,cav-3928,deg-3942
iye-2830,tel-3890
Urls hxxp://blah.com
hxxp://foo.com
hxxp://foo2.com
Some text I dont care about
More text i dont care about
.
.
Date 3/12/2013
You can use regular expressions like this :
$a = Get-Content C:\temp\file.txt
$b = [regex]::match($a, "^.*Codes (.*)Urls (.*)Some.*$", "Multiline")
$codes = $b.groups[1].value -replace '[ ]{2,}',','
$urls = $b.groups[2].value -replace '[ ]{2,}',','
If all files have the same structure you could do something like this:
$srcdir = "C:\Test"
$outfile = "$srcdir\out.csv"
$re = '^Name (.*(?:\r\n .*)*)\r\n' +
'Dept .*(?:\r\n .*)*\r\n' +
'Codes (.*(?:\r\n .*)*)\r\n' +
'Urls (.*(?:\r\n .*)*)' +
'[\s\S]*$'
Get-ChildItem $srcdir -Filter *.txt | % {
[io.file]::ReadAllText($_.FullName)
} | Select-String $re | % {
$f = $_.Matches | % { $_.Groups } | ? { $_.Index -gt 0 }
New-Object -TypeName PSObject -Prop #{
'Name' = $f[0].Value;
'Codes' = $f[1].Value;
'Urls' = $f[2].Value;
}
} | Export-Csv $outfile -NoTypeInformation

Resources