Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

I have a file in the following format.

abc|xyz|mno  
x up x  
up up x  
x x up  

abc|xyz|mno  
x x x  
up x x  

....  
....

Basically the first line of the file are device names (|ed) and following lines are states of the devices.

I would like the output to be in the following format:

abc,x|xyz,up|mno,x  
abc,up|xyz,up|mno,x  
abc,x|xyz,x|mno,up  

abc,x|xyz,x|mno,x  
abc,up|xyz,x|mno,x  

....  
....  

Do you have any pointers?

share|improve this question
    
It looks like you have a second header line in the middle of the file. Should that be ignored? Or can it possibly be different from the first one (and you want all subsequent output to use the new "device" names)? Also, do you guarantee that there will always be three things per line, or is the number of devices variable? –  G-Man Aug 12 '14 at 17:48
    
The lines contain sets of info. Each set containing a header of devices followed by states (X/up). And the number of lines per set is variable. And also the number of devices in the header also is variable. –  sudobash Aug 12 '14 at 20:22

2 Answers 2

up vote 1 down vote accepted

The following awk command should do what you want if the input is exactly as described (but it doesn’t do any error-checking):

awk '/\|/ {split($0, devices, "|"); next} /^$/ {print; next}
    {for (i=1; i<NF; i++) printf "%s,%s|", devices[i], $i; printf "%s,%s\n", devices[NF], $NF}'
  • If an input line contains a |, split it at the |s into an array called “devices”.  (We need to use \| because plain | means OR, as in /cat|dog/.)  Then go on to the next line of input data (i.e., don’t execute the following commands).
  • If an input line is blank, print it (the blank line) and go on to the next line without executing the following command.
  • For each line not matching one of the above, for each field up to but not including the last, print it with the corresponding device name and a | but no newline.  Then print the last field with a newline but no |.
share|improve this answer
    
Worked great. And this is what I was looking for! –  sudobash Aug 13 '14 at 17:43

If you can use perl, with List::MoreUtils module from CPAN:

$ perl -MList::MoreUtils=pairwise -F'\|' -anle '
    print and next if /^$/;
    @dev = @F and next if @F > 1;
    print join "|", pairwise {"$a,$b"} @dev,@{[split]};
' file
abc,x|xyz,up|mno,x
abc,up|xyz,up|mno,x
abc,x|xyz,x|mno,up

abc,x|xyz,x|mno,x
abc,up|xyz,x|mno,x

If you don't like using external module:

$ perl -F'\|' -anle '
    print and next if /^$/;
    @dev = @F and next if @F > 1;
    @state = split;
    print join "|", map {"$dev[$_],$state[$_]"} 0..$#dev;
' file
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.