7

I have large text file which contains data, formatted like this:

1
2
3
4
5
6
7
8
9
10

I am trying to convert it to this:

1           2             3
4           5             6
7           8             9
10

I tried awk:

'{ if (NR%2) {printf "%40s\n", $0} else {printf "%80s\n", $0} }' file.txt
1
  • 12
    And what happened with the awk? Feb 24, 2020 at 17:46

8 Answers 8

43

A solution with paste

seq 10 | paste - - -
1       2       3
4       5       6
7       8       9
10

paste is a Unix standard tool, and the standard guarantees that this works for at least 12 columns.

2
  • How to achieve the requested 40 char column width?
    – RudiC
    Feb 26, 2020 at 21:37
  • @RudiC Your answer correctly achieves that purpose. ;) My answer provides a solution to what is stated in the questions' title. Feb 27, 2020 at 11:19
8

The columns tool can do this:

$ seq 10 | columns -W 16 -c 3
1     2     3
4     5     6
7     8     9
10

-W 16 is just to set the line width to something small.

columns is not a Unix standard tool. It is part of GNU AutoGen.

Some versions of the more common column command may be able to set the number of columns with -c, but modern versions seem to have changed its meaning to set the line width by number of characters.

There's also pr as suggested by mpez0 in a comment:

$ seq 10 | pr -aT3
1           2           3
4           5           6
7           8           9
10

-aT3 is short for --across --omit-pagination --columns=3.

pr is in coreutils and POSIX, though -T/--omit-pagination seems to be GNU-specific.

8
  • 1
    this seems to work more or less the same way as the better known column command. Any reason to pick columns over column that you might be aware of?
    – iruvar
    Feb 25, 2020 at 23:11
  • 1
    @iruvar That column doesn't seem to have a way to specify number of columns.
    – JoL
    Feb 26, 2020 at 0:15
  • JoL, the -c flag exists to specify the number of display columns, which may or may not be different to the "number of columns" that columns allows to specify
    – iruvar
    Feb 26, 2020 at 14:24
  • @iruvar I'm not sure what you're getting at. The number of display columns is what we want. The actual number displayed may be less than specified if it otherwise would not fit in the set line width, which by default is 79, but other than that I don't see an issue.
    – JoL
    Feb 26, 2020 at 16:17
  • 2
    @iruvar Are you talking about column -c? That seems to be for setting the line width. According to the manpage: "Output is formatted to a width specified as number of characters." It was originally named --columns, but that name was deprecated in favor of --output-width. seq 10 | column -c 3 displays a single column. column -c 50 seems to limit lines to 50 characters.
    – JoL
    Feb 26, 2020 at 16:31
7

Try like

seq 1 10 | awk '{printf "%40s", $0} !(NR%3) {printf "\n"}'
                                       1                                       2                                       3
                                       4                                       5                                       6
                                       7                                       8                                       9
                                      10
8
  • 7
    print "" is briefer and more robust than printf "\n" since the former simply uses the value of ORS as intended while the latter uses the value you assume ORS will have but which it may not (e.g. it might be \r\n in some environments). The code has a problem though in that it won't produce a newline at the end of the text if the input itn's a multiple of 3 lines and so the output isn't a text file per POSIX and so YMMV with what subsequent POSIX tools will do with that output.
    – Ed Morton
    Feb 24, 2020 at 17:50
  • 3
    @EdMorton those environments are off topic here though, are there any *nix systems using something other than \n? In any case, you could even make the inverse argument: that printf '\n' is better because it prints a specific thing explicitly rather than making assumptions that could change based on the OS. I would be very annoyed if the system decided to add a \r\n to my file just because I happened to be on Windows or whatever.
    – terdon
    Feb 24, 2020 at 17:53
  • 2
    @EdMorton ah, no that was about the site rather than the question: Windows is explicitly off topic here, so we don't bother making things portable for any non-nix environment, so I was just wondering if you knew of a *nix system where the EOL isn't \n.
    – terdon
    Feb 24, 2020 at 17:59
  • 3
    @terdon "Windows is explicitly off topic here" - since when? Cygwin. WSL.
    – roaima
    Feb 24, 2020 at 18:04
  • 2
    @terdon again, it was just a general comment on how to make your code work with whatever value of ORS is in use at the time. I know there's something in cygwin where you can have RS at least set to \r\n depending on some other settings. Something to do with setting BINMODE=3 in gawk and whatever the underlying C implementation passes up to awk IIRC. I code to avoid being impacted by such issues so I don't have to look into them :-).
    – Ed Morton
    Feb 24, 2020 at 18:04
6

The tool for this job is rs ("reshape"):

$ seq 10 | rs 0 3
1   2   3
4   5   6
7   8   9
10  

We can change the column separator to a tab:

$ seq 10 | rs -C 0 3
1       2       3
4       5       6
7       8       9
10

Or right-align:

$ seq 10 | rs -j 0 3
 1   2   3
 4   5   6
 7   8   9
10

rs is not a Unix standard tool. It is widely available, however. It was invented in 4.2BSD, and so is in all of the modern BSDs. There are ports of it to Linux-based operating systems, such as to Debian, for example.

4
  • 1
    An answer with M. Kunze's rs should probably also use M. Kunze's jot. (-:
    – JdeBP
    Feb 25, 2020 at 12:49
  • 2
    There's also column from BSD 4.3. Feb 25, 2020 at 14:51
  • Is fmt another option to rs ?
    – WGroleau
    Feb 25, 2020 at 22:01
  • 1
    How about the pr command, from very early unix? pr -a --column=3
    – mpez0
    Feb 26, 2020 at 19:46
4

This will move the string width alternately between 40, 80, 120 for each line of input:

awk '{ m = (NR-1) % 3; i = (m+1) * 40; printf "%*s\n", i, $0 }'

Variables:

  • m - line number modulo 3 (i.e. 0, 1, 2 repeating)
  • i - the indent for the given value of m

In the absence of any other instruction, I've continued to use your own printf formatting so that each line of input is formatted as its own line of output, and each will be right-justified in the available space.

If you want three 40-column entries per line as shown in your example, rather than in your code, you could use this (change the 40s to -40s if you want left-justified text):

awk '{ printf "%40s", $0 } !(NR % 3) { printf "\n" }'
0

Tried with Below command , Tested and Worked fine

command

for ((i=0;i<=10;i++)); do awk -v i="$i" 'NR>i && NR<(i+4)' o| perl -pne "s/\n/ /g";echo -e  '\n'; i=$(($i+2)); done

output

1 2 3 

4 5 6 

7 8 9 

10 
2
  • On what system did this run?
    – RudiC
    Feb 25, 2020 at 21:18
  • on ubuntu system Feb 26, 2020 at 16:02
0

Using Python

#!/usr/bin/python
k=open('filename','r')
p=k.readlines()
for i in range(0,10,3):
    print str(p[i:i+3]).replace("[","").replace("]","").replace(",","").replace("\\n","").replace("'","")

output

1 2 3
4 5 6
7 8 9
10
-2

Personally, I would prefer the format

1  4  7
2  5  8
3  6  9

which can be achieved by

mkdir temp
cat file | while read FN; do
  touch "temp/$FN"
done
ls temp

though you might have to adjust window width to make it three columns. And you can’t redirect to a file; have to cut-n-paste. Redirection tells ls to do a single column.

1
  • -1 because the OP didn't ask for that format, and if you want it, that's not the way to do it (pipe input to pr -t3) anyway. Feb 26, 2020 at 20:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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