Reputation: 41
I am fairly new to UNIX coding and I have a file that I need to read in reverse line by line. The file has sections of code within {}. Then I need to run an awk script with this reversed file as the input. I am having our support staff install tac, but before they do I want to know if it will output what I need or if anyone can suggest an alternative.
The file has data in the form:
page 0 0 0 none
{
layer 0 0 0 "1line" 0 8 8 3
{
sector 0 -1 0 32
{
point_name 0 0 34543 34 44 1 1 0
status 1 0 1 0 1 431232 "transformer" 23 12
analog 1 0 1 0 1 321234 11.5 43 1 1 0
...
...
...
device 1 0 1 "ground" 32 56 1 1 0
}
}
}
and I want to preserve the {} but reverse the lines between the {} so the out put looks like:
page 0 0 0 none
{
layer 0 0 0 "1line" 0 8 8 3
{
sector 0 -1 0 32
{
device 1 0 1 "ground" 32 56 1 1 0
...
...
...
analog 1 0 1 0 1 321234 11.5 43 1 1 0
status 1 0 1 0 1 431232 "transformer" 23 12
point_name 0 0 34543 34 44 1 1 0
}
}
}
Also, does tac overwrite the input file or does it save it as a different file?
Upvotes: 4
Views: 1313
Reputation: 54402
tac
simply reverses the line order of a file, thus you'll need something more powerful to accomplish what you want. Also, all shell tools do not include means of overwriting an input file, without the use of 'temp' files. Some tools (e.g. sed
, Perl
, etc) have what's called an 'in-place editing' option, that, when a backup file suffix is not supplied, will give the net result of a file overwrite.
To accomplish what you want, I'd recommend using awk
. Run like:
awk -f script.awk file > newfile
To overwrite your input file, using a temporary file, run:
awk -f script.awk file > temp && mv temp file
Contents of script.awk
:
/{/ {
print (a ? a ORS : "") $0
f=1; a=b=""
next
}
f && !/}/ {
a = (a ? a ORS : "") $0
b = $0 (b ? ORS b : "")
next
}
/}/ && b {
print b ORS $0
a=b=f=""
next
}1
Results:
page 0 0 0 none
{
layer 0 0 0 "1line" 0 8 8 3
{
sector 0 -1 0 32
{
device 1 0 1 "ground" 32 56 1 1 0
...
...
...
analog 1 0 1 0 1 321234 11.5 43 1 1 0
status 1 0 1 0 1 431232 "transformer" 23 12
point_name 0 0 34543 34 44 1 1 0
}
}
}
Alternatively, here's the one liner:
awk '/{/ { print (a ? a ORS : "") $0; f=1; a=b=""; next } f && !/}/ { a = (a ? a ORS : "") $0; b = $0 (b ? ORS b : ""); next } /}/ && b { print b ORS $0; a=b=f=""; next }1' file
Explanation:
The first code block asks if the line contains an opening brace. If so, print anything in record 'a' followed by the current record (the line containing the opening brace). Set a flag, delete the records 'a' and 'b' and skip processing the rest of the code.
The second block will be executed if and only if the flag has been set and the line doesn't contain a closing brace. Now we build up two records 'a' and 'b'. Recall that record 'a' will be printed if a line contains an opening brace. So it must be built in the 'start to end' orientation. We then build record 'b' in the 'end to start' orientation. 'next' then skips processing the rest of the code.
The third block will be executed if the line contains a closing brace and the record 'b' (recall that this is the record build in the reverse orientation). We then print record 'b' followed by the current line (the line with the closing brace). Delete records 'a', 'b' and reset the flag. 'next' to skip processing the rest of the code. The '1' on the end enables default printing.
Upvotes: 1
Reputation: 85795
Like most UNIX (style) tools tac
neither overwrites the orignal file or writes a new one, it prints to stdout
, to write a new file with tac
you would use redirection tac file > newfile
this would save the reversed file
into newfile
. You cannot do what you require with tac
alone, you require a script.
Here is one in awk
:
{
# store each line in the array line
line[n++] = $0
}
/{/ {
# get the line number of the last line containing {
first=NR
}
!/}/ {
# get the line number of the last line not containing }
last=NR
}
END {
# print first section in order
for (i=0;i<first;i++)
print line[i]
# print second section in reverse order
for (i=last-1;i>=first;i--)
print line[i]
# print last section in order
for (i=last;i<NR;i++)
print line[i]
}
Save this to a file say reverse
then run awk -f reverse file
:
$ awk -f reverse file
page 0 0 0 none
{
layer 0 0 0 "1line" 0 8 8 3
{
sector 0 -1 0 32
{
device 1 0 1 "ground" 32 56 1 1 0
...
...
...
analog 1 0 1 0 1 321234 11.5 43 1 1 0
status 1 0 1 0 1 431232 "transformer" 23 12
point_name 0 0 34543 34 44 1 1 0
}
}
}
Use redirection to create a new file awk -f reverse file > newfile
.
Upvotes: 1