Good afternoon, there is a file with the following contents:

name1 PoweredOff 192.168.250.31 Debian GNU/Linux 6 (64-bit) Other 2.6.x Linux (64-bit) name2 PoweredOn 192.168.250.99 Debian GNU/Linux 6 (64-bit) Other 3.x Linux (64-bit) 

We need to bring it to mind:

 |name1|PoweredOff|192.168.250.31|Debian GNU/Linux 6 (64-bit)|Other 2.6.x Linux 64-bit)| |name2|PoweredOn|192.168.250.99|Debian GNU/Linux 6 (64-bit)|Other 3.x Linux (64-bit)| 

I tried to do this:

 IFS=$'\n'; for i in $(cat file); do if [[ -n $i ]]; then tr -s '\n' ' '; fi; done 

but it does not work

  • are entries separated by a blank line? - Mikhail Vaysman
  • No need to use bash for this! Besides the fact that for i in $(cat file) is wrong. - 0andriy

3 answers 3

With awk:

 awk -vRS='' -vFS='\n' -vOFS='|' '{$1=$1;print "",$0,""}' < data.txt 

The variable RS sets the delimiter between entries (empty line), FS is the field delimiter inside the entry (newline). Any command that changes any field (in this case, $1=$1 ) implicitly reassembles a record ( $0 ), using the value of OFS as a delimiter.

UPD : changed for an arbitrary number of lines in the record.

  • This is not bash , but awk . - 0andriy
  • 2
    @ 0andriy awk is included in the posix standard, it is present in any shell that supports it, which is bash. Actually awk can safely be considered its native command. - rjhdby
  • Alas, strictly speaking no. #!/bin/awk and go. It will be honest. The correct answer was given by @avp. - 0andriy

On bash, something like this:

 #!/bin/bash out="" while read s do if [ "$s" = "" ]; then [ "$out" = "" ] || echo "$out|" out="" else out="$out|$s" fi done [ "$out" = "" ] || echo "$out|" 

A few empty lines count for one, and also does not depend on the number of lines in a block (paragraph).

 avp@wubu:hashcode$ ./tt.sh <tt.txt |name1|PoweredOff|192.168.250.31|Debian GNU/Linux 6 (64-bit)|Other 2.6.x Linux (64-bit)| |name2|PoweredOn|192.168.250.99|Debian GNU/Linux 6 (64-bit)|Other 3.x Linux (64-bit)| avp@wubu:hashcode$ 

For use in another script, it might be better to arrange it as a function.

  • And also do not forget about the local keyword when used as a function. - 0andriy

With bash, this will be quite difficult, although possible.

It is much easier to use more suitable programs: awk, sed, etc.

An example for awk has already been given. Here is an example for the sed program:

 $ sed '/^$/d;N;N;N;N;s/\n/|/g;s/^/|/;s/$/|/' файл 

result:

 |name1|PoweredOff|192.168.250.31|Debian GNU/Linux 6 (64-bit)|Other 2.6.x Linux (64-bit)| |name2|PoweredOn|192.168.250.99|Debian GNU/Linux 6 (64-bit)|Other 3.x Linux (64-bit)| 

update

if the number of lines in blocks of text separated by an empty line is constant, then the above program for the sed interpreter can be adjusted by changing the number of N; statements N; .

if the number of lines is variable, then the program will have to be a little more complicated (there is probably a lot of excess, and it can be optimized):

 $ sed '/^$/d;:a;$!N;s/\n$/|/;tb;s/\n/|/;ta;:b;s/^/|/;$s/$/|/' файл 

when the file contains the following lines:

 name1 PoweredOff 192.168.250.31 Debian GNU/Linux 6 (64-bit) name2 PoweredOn 192.168.250.99 

then the output of the program will be:

 |name1|PoweredOff|192.168.250.31|Debian GNU/Linux 6 (64-bit)| |name2|PoweredOn|192.168.250.99| 
  • one
    Tightly attached to the format of 5 lines in one block - avp
  • @avp, and strictly speaking, it is sed , not bash . ( #!/bin/sed -f ...) - 0andriy
  • First, @avp is not tied to “5 lines in one block” format, and, moreover, “hard” - this is easily changed by the number of N; operators N; . secondly, since I got drunk, I added an option for a variable number of lines in a block. - aleksandr barakin
  • Great, but with a few blank lines before the block gets off. - avp
  • @avp, that you were calm, added another check. on this I propose to stop fantasizing about the fact that the author actually has a file. - aleksandr barakin