Refer to field and assign some logic to that field - awk

I have input file with data as
cell input out type fun level
AI20 A1,A2,A3 Z comb ((A1A2)) 2
INV I1 ZN comb (!I1) 1
BUF A1,A2,A3,B1 Z comb (!(((A1A2)A3)B1)) 4
CLK C Z seq Cq 1
XOR A1,A2,B1 Z comb (((A1A2)B1) 3
IAD A1,A2,A3 Z comb (!((A1A2)A3)) 3
INV I1 ZN comb (!I1) 1
I want output as below
cell ins input out
AI20 i1 .A1(A),.A2(B),.A3(C) .Z(n1)
INV i2 .I1(n1) .ZN(n2)
BUF i3 .A1(n2),.A2(1),.A3(0),.B1(0) .Z(n3)
CLK i4 .C(n3) .Z(n4)
XOR i5 .A1(n4),.A2(0),.B1(0) .Z(n5)
IAD i6 .A1(1),.A2(n5),.A3(0) .Z(n6)
INV i7 .I1(n6) .ZN(X)
The logic is
if it is first line of input file I.e AI20 assign its inputs A1,A2 to user defined A and B and assign it output to n1. For next line in input file assign previous net n1 to one of input and 0/1 to other inputs of that line and assign its output to next net n2 and so on. n1, n2,n3 so on are nets and can be assigned to an array which has no. Of rows equal to no of rows in (input file)-1(no. of rows exclude header row of input file). If it is last line or row of input file then assign that line or row output to X.
i1,i2,i3... are instance name which has number of rows equal to no of rows of (input file).
A,B,X are user defined. We can directly use them in code. The term inside bracket for input is basically the one present in the previous line in output(). Exception will be for first line input and last line output whose () terms are directly defined.
I used code
awk ' { print $1 ;print "."$2"()"; print "."$3"()" file }'
This gives me, but how can I build logic for inside().
AI20 .A1(), .A2(),.A3() .Z()

You may use this awk script:
cat remap.awk
NR == 1 {
print "cell ins input out"
next
}
id != "" {
++r
print id, "i" r, s, "." out "(n" r ")"
}
{
n = split($2, a, /,/)
s = ""
if (NR == 2) {
ch = 65
for (i=1; i<=n; ++i)
s = (s == "" ? "" : s ",") sprintf(".%s(%c)", a[i], ch++)
} else {
s = "." a[1] "(n" r ")"
for (i=2; i<=n; ++i)
s = s "," sprintf(".%s(%d)", a[i], i%2)
}
id = $1
out = $3
}
END {
if (r)
print id, "i" r+1, s, "." out "(X)"
}
Then use it as:
awk -f remap.awk file.txt | column -t
cell ins input out
AI20 i1 .A1(A),.A2(B),.A3(C) .Z(n1)
INV i2 .I1(n1) .ZN(n2)
BUF i3 .A1(n2),.A2(0),.A3(1),.B1(0) .Z(n3)
CLK i4 .C(n3) .Z(n4)
XOR i5 .A1(n4),.A2(0),.B1(1) .Z(n5)
IAD i6 .A1(n5),.A2(0),.A3(1) .Z(n6)
INV i7 .I1(n6) .ZN(X)

Related

Find and replace and move a line that contains a specific string

Assuming I have the following text file:
a b c d 1 2 3
e f g h 1 2 3
i j k l 1 2 3
m n o p 1 2 3
How do I replace '1 2 3' with '4 5 6' in the line that contains the letter (e) and move it after the line that contains the letter (k)?
N.B. the line that contains the letter (k) may come in any location in the file, the lines are not assumed to be in any order
My approach is
Remove the line I want to replace
Find the lines before the line I want to move it after
Find the lines after the line I want to move it after
append the output to a file
grep -v 'e' $original > $file
grep -B999 'k' $file > $output
grep 'e' $original | sed 's/1 2 3/4 5 6/' >> $output
grep -A999 'k' $file | tail -n+2 >> $output
rm $file
mv $output $original
but there is a lot of issues in this solution:
a lot of grep commands that seems unnecessary
the argument -A999 and -B999 are assuming the file would not contain lines more than 999, it would be better to have another way to get lines before and after the matched line
I am looking for a more efficient way to achieve that
Using sed
$ sed '/e/{s/1 2 3/4 5 6/;h;d};/k/{G}' input_file
a b c d 1 2 3
i j k l 1 2 3
e f g h 4 5 6
m n o p 1 2 3
Here is a GNU awk solution:
awk '
/\<e\>/{
s=$0
sub("1 2 3", "4 5 6", s)
next
}
/\<k\>/ && s {
printf("%s\n%s\n",$0,s)
next
} 1
' file
Or POSIX awk:
awk '
function has(x) {
for(i=1; i<=NF; i++) if ($i==x) return 1
return 0
}
has("e") {
s=$0
sub("1 2 3", "4 5 6", s)
next
}
has("k") && s {
printf("%s\n%s\n",$0,s)
next
} 1
' file
Either prints:
a b c d 1 2 3
i j k l 1 2 3
e f g h 4 5 6
m n o p 1 2 3
This works regardless of the order of e and k in the file:
awk '
function has(x) {
for(i=1; i<=NF; i++) if ($i==x) return 1
return 0
}
has("e") {
s=$0
sub("1 2 3", "4 5 6", s)
next
}
FNR<NR && has("k") && s {
printf("%s\n%s\n",$0,s)
s=""
next
}
FNR<NR
' file file
This awk should work for you:
awk '
/(^| )e( |$)/ {
sub(/1 2 3/, "4 5 6")
p = $0
next
}
1
/(^| )k( |$)/ {
print p
p = ""
}' file
a b c d 1 2 3
i j k l 1 2 3
e f g h 4 5 6
m n o p 1 2 3
This might work for you (GNU sed):
sed -n '/e/{s/1 2 3/4 5 6/;s#.*#/e/d;/k/s/.*/\&\\n&/#p};' file | sed -f - file
Design a sed script by passing the file twice and applying the sed instructions from the first pass to the second.
Another solution is to use ed:
cat <<\! | ed file
/e/s/1 2 3/4 5 6/
/e/m/k/
wq
!
Or if you prefer:
<<<$'/e/s/1 2 3/4 5 6/\n.m/k/\nwq' ed -s file

How to print out lines starting with keyword and connected with backslash with sed or awk

For example, I'd like to print out the line starting with set_2 and connected with \ like this. I'd like to know whether it's possible do to it with sed, awk or any other text process command lines.
< Before >
set_1 abc def
set_2 a b c d\
e f g h\
i j k l\
m n o p
set_3 ghi jek
set_2 aaa bbb\
ccc ddd\
eee fff
set_4 1 2 3 4
< After text process >
set_2 a b c d\
e f g h\
i j k l\
m n o p
set_2 aaa bbb\
ccc ddd\
eee fff
Try the following:
awk -v st="set_2" '/^set/ {set=$1} /\\$/ && set==st { prnt=1 } prnt==1 { print } !/\\$/ { prnt=0 }' file
Explanation:
awk -v st="set_2" ' # Pass the set to track as a variable st
/^set/ {
set=$1 # When the line begins with "set", track the set in the variable set
}
/\\$/ && set==st {
prnt=1 # When we are in the required set block and the line ends with "/", set a print marker (prnt) to 1
}
prnt==1 {
print # When the print marker is 1, print the line
}
!/\\$/ {
prnt=0 # If the line doesn't end with "/". set the print marker to 0
}' file
Would you try the sed solution:
sed -nE '
/^set_2/ { ;# if the line starts with "set_2" execute the block
:a ;# define a label "a"
/\\[[:space:]]*$/! {p; bb} ;# if the line does not end with "\", print the pattern space and exit the block
N ;# append the next line to the pattern space
ba ;# go to label "a"
} ;# end of the block
:b ;# define a label "b"
' file
Please note the character class [[:space:]]* is inserted just because the OP's posted example contains whitespaces after the slash.
[Alternative]
If perl is your option, following will also work:
perl -ne 'print if /^set_2/..!/\\\s*$/' file
This simple awk command should do the job:
awk '!/^[[:blank:]]/ {p = ($1 == "set_2")} p' file
set_2 a b c d\
e f g h\
i j k l\
m n o p
set_2 aaa bbb\
ccc ddd\
eee fff
And with this awk :
awk -F'[[:blank:]]*' '$1 == "set_2" || $NF ~ /\$/ {print $0;f=1} f && $1 == ""' file
set_2 a b c d\
e f g h\
i j k l\
m n o p
set_2 aaa bbb\
ccc ddd\
eee fff
This might work for you (GNU sed):
sed ':a;/set_2/{:b;n;/set_/ba;bb};d' file
If a line contains set_2 print it and go on printing until another line containing set_ then repeat the first test.
Otherwise delete the line.

Remove duplicate elements of string1 from string2 in Tcl

I want to remove duplicate elements of string1 from string 2 and then output new string. My code works only if duplicate elements are in sequential order.
I want to work it any order of elements. Please advise.
Current Code:
set str1 "a 1 b 2 c 3 X Y Z"
set str2 "a 1 b 2 c 3 P Q R"
set results {}
set results [lmap a_elem $str1 b_elem $str2 {
if {$a_elem != $b_elem} {string cat $b_elem} else continue
}]
puts $results
Output of the following code :
P Q R
However, if
set str1 "a 1 b 2 c 3 X Y Z"
set str2 "P a 2 1 R c Q 3 b"
then Output will be : P a 2 1 R c Q 3 b
Basically same as str2 without the duplicate elimnation.
If you want to output those elements of the list in str2 that are nowhere in str1, you should first build a dictionary of the elements of str1 so that you can use efficient lookup (dicts are internally hash tables). You are strongly recommended to use a procedure for this as it makes the implementation rather more efficient.
proc removeItems {str1 str2} {
foreach item $str1 {
dict set items $item ""; # Value unimportant
}
lmap item $str2 {
if {[dict exists $items $item]} continue
string cat $item
}
}
puts [removeItems "a 1 b 2 c 3 X Y Z" "P a 2 1 R c Q 3 b"]
# P R Q
The code naturally assumes that the order of str2 is important.
If performance is not important, you can use the more straight forward:
set results [lmap elem $str2 {
if {$elem ni $str1} {string cat $elem} else continue
}]

awk: printing lines side by side when the first field is the same in the records

I have a file containing lines like
a x1
b x1
q xq
c x1
b x2
c x2
n xn
c x3
I would like to test on the fist field in each line, and if there is a match I would like to append the matching lines to the first line. The output should look like
a x1
b x1 b x2
q xq
c x1 c x2 c x3
n xn
any help will be greatly appreciated
Using awk you can do this:
awk '{arr[$1]=arr[$1]?arr[$1] " " $0:$0} END {for (i in arr) print arr[i]}' file
n xn
a x1
b x1 b x2
c x1 c x2 c x3
q xq
To preserve input ordering:
$ awk '
{
if ($1 in vals) {
prev = vals[$1] " "
}
else {
prev = ""
keys[++k] = $1
}
vals[$1] = prev $0
}
END {
for (k=1;k in keys;k++)
print vals[keys[k]]
}
' file
a x1
b x1 b x2
q xq
c x1 c x2 c x3
n xn
What I ended up doing. (The answers by Ed Morton and Jonte are obviously more elegant.)
First I saved the 1st column of the input file in a separate file.
awk '{print $1}' input.file.txt > tmp0
Then saved the input file with lines, which has duplicate values at $1 field, removed.
awk 'BEGIN { FS = "\t" }; !x[$1]++ { print $0}' input_file.txt > tmp1
Then saved all the lines with duplicate $1 field.
awk 'BEGIN { FS = "\t" }; x[$1]++ { print $0}' input_file.txt >tmp2
Then saved the $1 fields of the non-duplicate file (tmp1).
awk '{ print $1}' tmp1 > tmp3
I used a for loop to pull in lines from the duplicate file (tmp2) and the duplicates removed file (tmp1) into an output file.
for i in $(cat tmp3)
do
if [ $(grep -w $i tmp0 | wc -l) = 1 ] #test for single instance in the 1st col of input file
then
echo "$(grep -w $i tmp1)" >> output.txt #if single then pull that record from no dupes
else
echo -e "$(grep -w $i tmp1) \t $(grep -w $i tmp2 | awk '{
printf $0"\t" }; END { printf "\n" }')" >> output.txt # if not single then pull that record from no_dupes first then all the records from dupes in a single line.
fi
done
Finally remove the tmp files
rm tmp* # remove all the tmp files

Group by Pairs all textbox input vb.net 2010

I have a textbox, I want to group all input two by two.
Input: E5D3DFOXJFUIOXZJDFCNIUEBSKDLFJCNESODFKJ
I want to become: E5 D3 DF OX JF UI OX ZJ DF CN IU EB SK DL FJ CN ES OD FK J
How can I do that?
I have this function but it doesn't really work :
For i As Integer = TextBox1.Text.Length - 2 To 2 Step -2
TextBox1.Text = TextBox1.Text.Insert(i, " ")
Next
It gives me something like that :
E5D 3D FO XJ FU IO XZ JD FC NI UE BS KD LF JC NE SO DF KJ
or when the string is too long it's like that :
E5D 3D FO XJ FU IO XZ JD F C NI UE BS K D LF JC NE SO DF KJ
Anyone can help me with this?
Please excuse the language switch to C#. Not particularly elegant, but the following code should work for both even and odd lengths of string
string buffer = String.Empty;
for (int i = 0; i < textBox1.Text.Length; i += 2)
{
// Exclude the case where 1 or 2 remaining chars here (no trailing space)
if (textBox1.Text.Length - i > 2)
{
buffer += textBox1.Text.Substring(i, 2) + " ";
}
else
{
buffer += textBox1.Text.Substring(i);
}
}
textBox1.Text = buffer;
Thanls you for reply, C# or VB.net it's the same :-) in vb2010 it will be :
Dim buffer As String = [String].Empty
For i As Integer = 0 To TextBox1.Text.Length - 1 Step 2
' Exclude the case where 1 or 2 remaining chars here (no trailing space)
If TextBox1.Text.Length - i > 2 Then
buffer += TextBox1.Text.Substring(i, 2) & " "
Else
buffer += TextBox1.Text.Substring(i)
End If
Next
TextBox1.Text = buffer
K.N.A.82.A.C.M