I'm having the unix output as

ABC : 123
DEFG : 4587
MJk : 36

I want to combine all the rows in single row with same separation like

ABC DEFG MJK : 123 4587 36

I know to use awk to print output in single coloumn i.e.

awk'{print $1}' | tr '\n' ' ' 

but it is printing


not the values 123 4587 36

  • So what is an actual problem here? It seems that awk is doing the job.
    – DevilaN
    Jun 19, 2020 at 9:29
  • it is only giving me the out put as ABC DEFG MJK not the values.
    – avi
    Jun 19, 2020 at 9:33
  • that will print ABC : 123 DEFG : 4587 MJK : 36 not like I wanted
    – avi
    Jun 19, 2020 at 9:38
  • Is the order of the output important? I mean, does it need to be ABC DEFG MJK : 123 4587 36 or would ABC MJk DEFG : 36 123 4587 be OK?
    – terdon
    Jun 19, 2020 at 9:44
  • order is important
    – avi
    Jun 19, 2020 at 9:46

7 Answers 7

$ awk -F: '{a=a $1; b=b $2} END{print a FS b}' file
ABC DEFG MJk : 123 4587 36

We're seeing a lot of posts recently where people use tr '\n' ' ' or similar to convert newlines to something else. Except in rare situations don't do that as it converts a POSIX text file (that all POSIX text processing tools can read) into something else where YMMV. POSIX text lines end in \n and a POSIX txt file is made up of POSIX text lines. If you use tr or anything else to remove all newlines then what any subsequent POSIX text processing tool (awk, sed, etc., etc.) might do with that as input is undefined behavior.

Here's an example of some other behavior you might not expect but is actually defined by POSIX. Lets say we want to convert this multi-line string into a single space-separated line:

$ printf 'foo\nbar\n' | wc -l

using tr to remove all \ns:

$ printf 'foo\nbar\n' | tr '\n' ' '
foo bar $
$ printf 'foo\nbar\n' | tr '\n' ' ' | wc -l

vs a better way to do the same that outputs a POSIX text file and so gives a more intuitive result when piped to wc:

$ printf 'foo\nbar\n' | paste -sd ' ' -
foo bar
$ printf 'foo\nbar\n' | paste -sd ' ' - | wc -l

This can be done usin the sed editor by appending the next line into tge pattern space and shuffling the fiekds around the center colon. The test command loops till the eof and inneach iteration the next line is picked.

sed -Ee '
' file

The awk version is self explanatory.

awk -F ':' -v ORS='' '
{ a[NR] = $1; b[NR] = $2 }
  a[NR] = a[NR] FS
  for (i=1; i<=2*NR; i++)
     print i<=NR ? a[i] : b[i-NR]
  print RS
' file


ABC DEFG MJK : 123 4587 36

It would seem that you need to buffer the entire content. You could try the following:

awk '{FNR==1{first=$1; second=$3} FNR>1{first=first " " $1; second=second " " $3} END{printf("%s : %s\n", first, second)}'

This will accumulate the contents of all "first column entries" of all lines in the variable first, and those of the "second column entries" (actually the third, because the : will form a column of its own from awks point of view) in the variable second. In the end, it prints the buffers so accumulated, with a : as separator character.

If leading/trailing whitespace is not an issue, you can shorten the code to

awk '{first=first " " $1; second=second " " $3} END{printf("%s : %s\n", first, second)}'
  • You can use xargs to trim repeating whitespaces and remove them from start and end of output, like I've done in my not so elegant answer :)
    – DevilaN
    Jun 19, 2020 at 10:15

This can be done in different ways, but I leave my answer as an example without using awk.

Lets assume that your data is in file data.dat, then solution is:

echo "$(cut -d ':' -f 1 data.dat | tr '\n' ' ' | xargs) : $(cut -d ':' -f 2 data.dat | tr '\n' ' ' | xargs)"

I've got following result:

ABC DEFG MJk : 123 4587 36

Lets cut to the explanation:

  • cut -d ':' -f 1 data.dat
    • cut is command to get part of line (see man cut)
    • -d ':' tells that we are using colon as delimiter
    • -f 1 tells to extract only first column (all what is left from colon in your case)
    • data.dat is simply the name of file when your original data is being stored
  • tr '\n' ' ' translates changes newlines (\n) to spaces joining output strings together as one line
  • xargs is here as a trick - without any args this command reprints each word trimming extra white spaces

It is almost the same with second part, except that -f 2 is used to extract string on the left of colon mark.

We are using echo command to echo results ($(...) expands command and places output of command inside brackets as a result) from two parses of file and join them with colon mark between two passes.

  • 1
    @AdminBee: Thanks for pointing it out. Changed. It seems that old habits die hard...
    – DevilaN
    Jun 19, 2020 at 10:23

I would rather to store each column in an array and then print them in desired format

awk -F" : " '{a[NR]=$1;b[NR]=$2} END{ for(i=1;i<=NR;i++) printf "%s ",a[i]; printf ": "; for(i=1;i<=NR;i++) printf "%s ",b[i];}' file
  • -F" : " separates the columns based on " : " delimiter
  • {a[NR]=$1;b[NR]=$2} creates two arrays indexes = NR (number of rows) and values = value of the column in that row
  • printf prints in desired order (printf will not print a newline)

cut and paste (and <()):

paste -s -d ' ' <(cut -d : -f 1 file) <(cut -d : -f 2 file) | paste -s -d :

Read file twice, cut to relevant fields. paste serially (twice, first separated by space, then by colon). paste -s will replace the <newline> of every line except the last line with the delimiter. If the spaces are already in the input file, change the first paste delimiter to an empty string -d ''.

a=`awk -F ":" '{print $1}'  filename | perl -pne "s/\n/ /g"`
b=`awk -F ":" '{print $2}'  filename | perl -pne "s/\n/ /g"`
awk -v a="$a" -v b="$b" '{print a":"b;exit}'


ABC  DEFG  MJk  : 123  4587  36


for i in k:
print " ".join(a) +": "+ " ".join(b)


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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