Reshaping text
September 23, 2022 at 10:10 AM by Dr. Drang
It’s not often that I run into a new (to me) Unix command. But when I had a problem yesterday, I came across a simple solution using the rs
command, which I’d never heard of before. Maybe you’ll find it useful, too.
As I mentioned in a post last year, I often have deal with lists of alphanumeric strings. Usually, these are serial numbers, but the lists I was dealing with yesterday were lists of apartment numbers in a high-rise building. After various manipulations and comparisons, I had two lists that I wanted to send to a client in an email so we could discuss them. I could have put the lists into a spreadsheet, but that would mean the client would have to open the email and then open the attachment. If you knew the level of computer literacy my clients have, you would understand why I wanted to avoid that and just have the lists in the body of the email.
The problem was that the lists were long. One had 84 items and the other had 30. Pasting them into the message in the form I had them—one item per line—would take up too much space. Pasting them in as a comma-separated series—which I could convert them to with a simple search-and-replace—would be very hard to read. What I wanted was nicely formatted set of rows and columns that would be easy to read without taking up too much space. But I couldn’t think of a quick, automated way to put the list into that form.1
After some Googling, I came across this Ask Ubuntu question, which led me to rs
, an old BSD command that comes installed on macOS. The name stands for “reshape,” and it will take any row/column set of data and rearrange it into a different number of rows and columns. Because my lists were a special case—just one column each—they fit the input requirements.
With my list of 84 items on the clipboard, I tried the simplest form of the command,
pbpaste | rs 14
and got this output:
203 204 207 215 302 306
1502 1507 1509 1510 1512 1515
1610 1700 1701 1704 1705 1706
1710 1801 1803 1806 1809 1815
1903 1905 2000 2001 2002 2004
2009 2101 2102 2103 2105 2106
2108 2112 2201 2203 2208 2209
2302 2306 2307 2309 2406 2407
2503 2504 2506 2509 2515 2606
3003 3007 3008 3009 3100 3201
3202 3205 3208 3300 3301 3415
3500 3505 3506 3600 3606 3700
3706 3715 3807 3815 3900 3907
4009 4111 4115 4203 4306 4308
The first argument to rs
is the number of rows in the reshaped output. Since 84 is the product of 6 and 14, I got 14 rows of 6 items each. This was fine, but I wanted a little more room between the columns. In rs
parlance, the intercolumn space is called the gutter, and you set the size of the gutter with the -g
option. So
pbpaste | rs -g5 14
gave me
203 204 207 215 302 306
1502 1507 1509 1510 1512 1515
1610 1700 1701 1704 1705 1706
1710 1801 1803 1806 1809 1815
1903 1905 2000 2001 2002 2004
2009 2101 2102 2103 2105 2106
2108 2112 2201 2203 2208 2209
2302 2306 2307 2309 2406 2407
2503 2504 2506 2509 2515 2606
3003 3007 3008 3009 3100 3201
3202 3205 3208 3300 3301 3415
3500 3505 3506 3600 3606 3700
3706 3715 3807 3815 3900 3907
4009 4111 4115 4203 4306 4308
Note that the number used for the gutter size has to come immediately after the -g
. You can’t leave a space between the option and its argument as you can with many other commands.
Again, this was nice, but I thought it would be better if the list were laid out column-by-column instead of row-by-row. That called for the -t
(transpose) option,
pbpaste | rs -g5 -t 14
which gave me
203 1701 2002 2302 3008 3606
204 1704 2004 2306 3009 3700
207 1705 2009 2307 3100 3706
215 1706 2101 2309 3201 3715
302 1710 2102 2406 3202 3807
306 1801 2103 2407 3205 3815
1502 1803 2105 2503 3208 3900
1507 1806 2106 2504 3300 3907
1509 1809 2108 2506 3301 4009
1510 1815 2112 2509 3415 4111
1512 1903 2201 2515 3500 4115
1515 1905 2203 2606 3505 4203
1610 2000 2208 3003 3506 4306
1700 2001 2209 3007 3600 4308
Finally, I wanted the three-digit numbers properly aligned. You right-justify the items within their columns using the -j
option,
pbpaste | rs -g5 -tj 14
which prints out
203 1701 2002 2302 3008 3606
204 1704 2004 2306 3009 3700
207 1705 2009 2307 3100 3706
215 1706 2101 2309 3201 3715
302 1710 2102 2406 3202 3807
306 1801 2103 2407 3205 3815
1502 1803 2105 2503 3208 3900
1507 1806 2106 2504 3300 3907
1509 1809 2108 2506 3301 4009
1510 1815 2112 2509 3415 4111
1512 1903 2201 2515 3500 4115
1515 1905 2203 2606 3505 4203
1610 2000 2208 3003 3506 4306
1700 2001 2209 3007 3600 4308
I did a similar thing with the list of 30 and then sent off a nice, compact email.
I should mention that rs
is reasonably smart about leaving blanks at the ends of rows or columns. If there were 79 items in my list instead of 84,
pbpaste | rs -g5 -tj 14
would give
203 1701 2002 2302 3008 3606
204 1704 2004 2306 3009 3700
207 1705 2009 2307 3100 3706
215 1706 2101 2309 3201 3715
302 1710 2102 2406 3202 3807
306 1801 2103 2407 3205 3815
1502 1803 2105 2503 3208 3900
1507 1806 2106 2504 3300 3907
1509 1809 2108 2506 3301 4009
1510 1815 2112 2509 3415
1512 1903 2201 2515 3500
1515 1905 2203 2606 3505
1610 2000 2208 3003 3506
with the empty “cells” at the end of the last column. Without the -t
option,
pbpaste | rs -g5 -j 14
gives
203 204 207 215 302 306
1502 1507 1509 1510 1512 1515
1610 1700 1701 1704 1705 1706
1710 1801 1803 1806 1809 1815
1903 1905 2000 2001 2002 2004
2009 2101 2102 2103 2105 2106
2108 2112 2201 2203 2208 2209
2302 2306 2307 2309 2406 2407
2503 2504 2506 2509 2515 2606
3003 3007 3008 3009 3100 3201
3202 3205 3208 3300 3301 3415
3500 3505 3506 3600 3606 3700
3706 3715 3807 3815 3900 3907
4009
with the empty cells at the end of the last row.
There are lots of options for dealing with different types of input and generating different types of output. I don’t see any value in trying to remember them; I can look them up in the man page as needed. The most important thing is to know that rs
exists.
-
Yes, I could use column selection in BBEdit to edit my lists into rows and columns by hand, but if you think I’d do that, you haven’t been reading this blog very long. ↩