7

I'm using bash (CentOS 5) and looking to generate this output (I assume I could use seq or echo together maybe?):

1|1,
2|2,
3|3,
....
31|31,
32|32,
33|33,

I googled seq examples for over two hours and the closest I can come up with is:

echo {1..31}..{1..31} | tr ' ' '\n'

which almost gives me what I want, but messes up when I change the .. to | or even "|").

A second number generation I need is formatted the same way but for descending years, i.e.:

2015|2015,
2014|2014,
...
1938|1938,
1937|1937,
1936|1936,

I've already manually typed out these two lists, but I would love any input on how I could have done this from the command line for future needs and to learn (seq or echo, I'm assuming).

1
  • Is the comma at the end of each line part of the output you want?
    – dhag
    Commented Dec 23, 2015 at 20:12

8 Answers 8

10

The following should do it:

seq 0 31 | awk '{ print $1"|"$1", " }'

in the descending case:

seq 31 -1 0 | awk '{ print $1"|"$1", " }'

These use awk to duplicate the number on each line, separated by a pipe character.

Or using pure bash (as suggested by DopeGhoti in a comment):

for n in {0..31}; do printf "%d|%d,\n" $n $n; done
for n in {31..0}; do printf "%d|%d,\n" $n $n; done
4
  • 4
    Why fork out to two binaries? for i in {0..31}; do echo "$i|$i,"; done.
    – DopeGhoti
    Commented Dec 23, 2015 at 20:25
  • There's more than one way to do it, for sure. I'll edit my answers to add more solutions.
    – dhag
    Commented Dec 23, 2015 at 20:28
  • 1
    It's bash, there's always more than one way to do it (:
    – DopeGhoti
    Commented Dec 23, 2015 at 20:34
  • 3
    @DopeGhoti - why not fork two tiny binaries that do the job much faster than the slow, monolithic shell - which can only do it with two for loops - one which generates the set internally, and the second which then iterates over it in the script. for ... {num..num} is an ugly way to do anything.
    – mikeserv
    Commented Dec 23, 2015 at 21:36
10

My twists:

  • pure awk

    awk 'BEGIN{for(;n<31;)print ++n"|"n","}'
    
  • printf + xargs

    printf '%s\n' {0..31} | xargs -I {} echo "{}|{},"
    
  • repeat loop in zsh

    n=0; repeat 32 echo "$n|$((n++)),"
    
7
seq 100|sed 's/.*/&|&,/'

...should do it fine...

With just a shell:

i=-1 x=31
while [ "$i" -lt "$x" ]
do    echo "$((i+=1))|$i,"
done
1
  • Awesome! thank you very much this does exactly what im looking for.. now im going to hit the man pages and see how.
    – James Gaul
    Commented Dec 23, 2015 at 21:28
7

And for fun with paste:

paste -d'|,' <(seq 31) <(seq 31) /dev/null
6

The simplest solution (in ksh93, bash, zsh):

printf '%s|%s,\n' {1..31}{,}             ### Ascending  sequence.
printf '%s|%s,\n' {2015..1936}{,}        ### Descending sequence.

Or, if you must use a loop:

i=0 x=31
while    (( i++ < x ))
do       echo "$i|$i,"
done
1
  • Clever use of {,}, really nice!
    – jimmij
    Commented Dec 24, 2015 at 11:17
5

Some convoluted ones:

seq  -f %.0f 0.9 0.5 33.8 | paste -d '|,\n' - - /dev/null
seq  -f %.0f 2015.1 -0.5 1935.2 | paste -sd '|,\n' - - /dev/null

Note that seq is not a standard command. POSIXly, if you want to print a sequence a number, the best tools are awk and bc. bc can do arbitrary precision arithmetic but is very limited when it comes to format its output, so awk is a more obvious solution here.

awk 'BEGIN{for(i=1;i<=33;i++) print i "|" i ","}'

Though you could use bc like:

echo 'for (i=1;i<=33;i++) {i;i}' | bc | paste -d '|,\n' - - /dev/null
0
3

Using perl you may do both the seq loop and the print formatting in a single process:

perl -e 'for (1..31) {print "$_|$_,\n"}'
2

It doesn't strictly answer the "from the command line" part, but since you say you manually typed out the lists, allow me to present: The Easy and Fast Way to type out your lists, using vim.


The Long Version

Open up vim by typing: vim at the command line.

Enter insert mode by pressing i. Type the first line: 1|1, and press <Esc> to exit insert mode.

Press q to begin recording a macro, and press x (arbitrarily) to record the macro into register "x".

Type yy to copy the current line and p to paste it. This will also put your cursor at the beginning of the newly pasted line. Then, type <C-a> (Control-A) to increment the number under the cursor.

Type t, to move your cursor forward to just before the next comma (which puts it on the second number on the line). Type <C-a> again to increment this number also.

Type q to stop recording the macro.

Now all that remains is to play back the macro as many times as you want. We'll say 15, just for demonstration. Type 15@x and your macro will be replayed 15 times, so your screen will now show your list from 1 to 17. Then if you want more lines, you can just type, for instance, 500@x.

If you have too many lines, type the letter k to move up in the file and j to go down. When you are on the first line to be deleted, you can delete from there to the end of the file by typing dG.


The Short Version

To generate the list from 1 to 31, open vim by typing vim<Enter> at the command prompt, then press the following keys:

i1|1,<Esc>qxyyp<C-a>t,<C-a>q29@x


I'm fully familiar with seq, tr, echo and I understand the various other answers that have been posted. It's my considered opinion that the best tool for this job is vim, unless you actually need to script the text generation.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .