Making lists with jot and seq

The Mac comes with two command-line tools for making sequential lists: jot and seq. In the past, I’ve tended to use jot, mainly because that’s the one I learned first. But seq can do almost everything jot can, and its argument ordering makes more sense in many situations.

The point of both commands is to generate a list of strings that include sequential numbers.1 At their most basic, jot and seq each produce a set of lines with numbers that count up from one. Both

jot 5

and

seq 5

produce the same output:

1
2
3
4
5

More interesting results come from adding arguments and options.

Arguments

jot can take up to four arguments; seq up to three. Here’s a table of how the arguments are ordered.

Arguments jot seq
1 count last
2 count first first last
3 count first last first step last
4 count first last step

As you can see, additional arguments get added to the end for jot but are inserted for seq. As we’ve seen above, for the one-argument case, jot’s count and seq’s last are effectively the same, because both commands have default starting points and step sizes of 1.2

For the two-argument case, both commands keep the default step size as 1 but allow you to change the starting point. To generate numbers from 5 through 10, you’d use either

jot 6 5

or

seq 5 10

To me, the seq arguments are easier to remember and a more natural expression of what’s intended. It’s easy to make an off-by-one error and try

jot 5 5

which will get you only up to 9.

As far as I’m concerned, jot with three arguments is worthless. If I know the starting and stopping points, and the step size is still at its default of one, the count parameter is redundant. You can avoid doing the subtraction in your head (and then remembering to add 1 to avoid the off-by-one error) by filling the count slot with a hyphen:

jot - 5 10

This is fine, but seq 5 10 is simpler.

The three-argument seq and the four-argument jot allow you to specify the step size. Again, jot’s count is redundant and it’s best to put a hyphen in its place to allow the computer to do the arithmetic. To get from 10 to 40 counting by 5s, enter

jot - 10 40 5

to get

10
15
20
25
30
35
40

The seq command to get the same result is

seq 10 5 40

This is the one place where I find seq’s arguments hard to remember. So many programming languages use the order first, last, step that my fingers do that before I can stop them. There’s nothing illogical about the first, step, last ordering, but it’s not what I’m used to.

Options

Just printing out lines with numbers is dull. The real value of jot and seq comes from applying options (often called switches) that allow us format the numbers and include them in other text. Here are the options for jot and seq:

Option jot seq
output format -w string -f string
repeated string -b string
equal width -w
separator -s string -s string
omit final newline -n
precision -p decimals
characters -c
random -r

The option I use most often is -w for jot and the nearly equivalent -f for seq. In both cases, the string argument is a printf-style formatting string. So if I wanted to generate a bunch of sequential file names, I could use

jot -w "file%02d.txt" 5

or

seq -f "file%02.0f.txt" 5

to get3

file01.txt
file02.txt
file03.txt
file04.txt
file05.txt

Here’s a situation where jot is a little more sensible. Even though both commands treat the numbers they generate as floating point, jot recognizes when the numbers have no decimal part and allows you to use the %d format specifier. seq forces you to use a floating point specifier, like %f or %e, which then forces you to explicitly state that the number of digits after the decimal point is 0. If you don’t, you’ll get a mess. For example,

seq -f "file%02f.txt" 5

returns

file1.000000.txt
file2.000000.txt
file3.000000.txt
file4.000000.txt
file5.000000.txt

The -b option for jot will repeat the given string as many times as specified. The string isn’t a formatting string and won’t include the numbers generated by jot.

jot -b Hello 4

returns

Hello
Hello
Hello
Hello

which I guess could be useful. You might think you could use the -w option with a string that doesn’t include a formatting specifier. But if you try that, jot will add the number to the end anyway.

jot -w Hello 4

returns

Hello1
Hello2
Hello3
Hello4

which is kind of presumptuous of it.

seq is better behaved.

seq -f Hello 4

returns

Hello
Hello
Hello
Hello

just as you’d expect.

By the way, if you want just a list of numbers, but you want them zero-padded to be of the same character length, seq has you covered:

seq -w 8 12

returns

08
09
10
11
12

You could achieve the same thing with a formatting string, but the -w option is a little shorter.

We’ve been looking at output where each item in the sequence is on its own line—i.e., separated by newlines—but both commands have an -s option for specifying another separator. The option works a little differently for the two commands.

jot -s, 5 8

gives

8,9,10,11,12

whereas

seq -s, 8 12

gives

8,9,10,11,12,

You see that seq’s “separator” (that’s what it’s called in the man page) is really a suffix. To get the same output as jot, you can pipe the output through sed to delete the last character:

seq -s, 8 12 | sed 's/.$//'

Kind of annoying.

One advantage seq has over jot with regard to separators is that it understands tabs.

seq -s "\t" 8 12 | sed 's/.$//'

will give you the numbers separated by tab characters. But jot treats the backslashed t literally.

jot -s "\t" 5 8

will give you

8\t9\t10\t11\t12

Not helpful.

One more thing related to -s. Because jot treats the separator as an actual separator, the newline that’s added by default to the end of the output isn’t handled by -s. If you don’t want jot to add it, including the -n option will omit it. This is just like the -n option to echo.

As mentioned above, both commands handle floating point numbers, but they have different defaults.

seq -w 7 .5 9

returns

7.0
7.5
8.0
8.5
9.0

as you would expect. But the similar

jot - 7 9 .5

returns

7
8
8
8
9

which seems bizarre. The trick is that jot has a default precision of zero decimal places, so it’s rounding to the nearest integer. The default precision can be changed with the -p option.

jot -p1 - 7 9 .5

returns

7.0
7.5
8.0
8.5
9.0

which is almost certainly what you wanted.

Finally, I want to talk about one clear advantage that jot has over seq: the ability to generate sequences of characters using the -c option.

jot -c 5 65

returns

A
B
C
D
E

What it’s doing is interpreting each number as an ASCII code and returning its corresponding character. Even better, you can use character arguments:

jot -c - a e

returns

a
b
c
d
e

You can get the same effect by using the %c formatting specifier in the -w option. I’ve used this feature of jot to generate apartment addresses when working with a building that uses letters for its units. For example, say I am dealing with a five-story building in which the apartments start on the second floor and are given the letters A through D. I want to quickly generate a list of all the apartment addresses. The command is

for n in `seq 2 5`; do jot -w "Apt. $n%c" - A D; done

and the output is

Apt. 2A
Apt. 2B
Apt. 2C
Apt. 2D
Apt. 3A
Apt. 3B
Apt. 3C
Apt. 3D
Apt. 4A
Apt. 4B
Apt. 4C
Apt. 4D
Apt. 5A
Apt. 5B
Apt. 5C
Apt. 5D

As a practical matter, with only 16 apartments, I probably wouldn’t bother with seq or jot. It’s faster to just type out the apartment addresses when the number is small. But with larger buildings, a command like this saves a lot of time and insures that I don’t skip or duplicate addresses. As with any automation, accuracy is as important as the time saved.


  1. jot also has an option for generating random numbers, but I’ve never had any use for that. 

  2. That the counts start at 1 instead of 0 is an indication that these commands were written for normal humans, not programmers. 

  3. The quotation marks aren’t necessary in either of these cases because the shell won’t process the formatting string before sending it to jot or seq. But I have a hard time remembering which characters are special to the shell, so I tend to use quotation marks any time I have a string that includes potentially special characters like %