52

I have iterate over numbers in various order. I am able to display them in increasing order, even with steps like:

$ seq --separator="," 1 10
1,2,3,4,5,6,7,8,9,10
$ seq --separator="," 1 2 10
1,3,5,7,9

I am also able to display them in reverse order, neither continuous nor step wise.

$ seq --separator="," 10 1   
$ seq --separator="," 10 2 1

No output for above commands.

My shell details:

$ bash --version
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

Let me know how I would be able to display the numbers in descending order?

Daniel Serodio
  • 1,173
  • 1
  • 9
  • 14
mtk
  • 27,530
  • 35
  • 94
  • 130
  • 10
    For future readers, seq is a completely nonstandard tool and there is no guarantee that any two implementations will be the same. If you need to write a loop that iterates backwards over numbers in bash, use for ((i=$max;i>=0;i--)) … or the like. – kojiro Feb 15 '13 at 13:13

8 Answers8

65

use negative increment

seq -s, 10 -2 1
10,8,6,4,2
watael
  • 911
29

In general, you don't want to use seq, it's not portable (even among standard Linux environments). If you're using ksh, zsh, or bash4+, you can use brace expansion:

echo {10..1..2} | tr " " ,
10,8,6,4,2
Chris Down
  • 125,559
  • 25
  • 270
  • 266
  • 2
    That's short and quick, but I am on older bash version. – mtk Feb 15 '13 at 10:05
  • 23
    Sweet answer, but there's some irony when you point out seq is nonstandard and then use bash-4-only brace expansion. ;) – kojiro Feb 15 '13 at 13:15
  • @kojiro - No argument to be honest ;-) My main concern is not one of whether the command exists (that may or may not matter depending on whether the script is being distributed/etc), but whether the command executes as is expected by the author. bash4's brace expansion almost has that guaranteed (if it works, it works as you expect), whereas seq doesn't. – Chris Down Feb 16 '13 at 06:06
  • 1
    Why is it not portable? Do you have sources or proofs? I'm interested. – Benoit Duffez Sep 07 '15 at 07:50
15

Another way in pure bash, ksh or zsh:

for ((i=10;i>0;i-=2)) ; do echo -n "$i," ; done

A pure POSIX sh way:

i=10
while [ "$i" -gt 2 ]; do printf "$i,"; i=$((i-2)); done
echo "$i"
rush
  • 27,403
7

Now, standard POSIX ones:

awk 'BEGIN{for (i = 10; i > 0; i -= 2) print i}' | paste -sd , -

(interestingly, with mawk (and to a lesser extent gawk as well) a lot faster than GNU seq for i = 10000000 instead of i = 10)

Or

i=10; set --
while [ "$i" -gt 0 ]; do
  set -- "$@" "$i"
  i=$(($i - 2))
done
IFS=,
echo "$*"

(would only be more efficient with small numbers of iterations, especially with bash)

Or

echo 'for(i=10;i>0;i-=2) i' | bc | paste -sd , -

(which would support numbers of any size, but note that past a certain number of digits (numbers greater than 1070 in the POSIX locale at least), lines would be wrapped with backslashes)

  • 1
    In GNU bc, you can avoid the line wrap by setting BC_LINE_LENGTH=0 in the environment. No such luck on other implementations. – Gilles 'SO- stop being evil' Feb 15 '13 at 22:26
  • 1
    Why use the positional arguments rather than loop around s=$s,$i or call echo -n/echo \c/printf? – Gilles 'SO- stop being evil' Feb 15 '13 at 22:30
  • @Gilles'SO-stopbeingevil', printf is not guaranteed to be builtin, and echo can't be used here unless you know the exact variant you have (POSIX without XSI leaves the behaviour unspecified if the first argument is -n or any argument contain backslashes). s=$s,$i and stripping the last , or not adding it on the first run would be an option yes. – Stéphane Chazelas Dec 02 '22 at 13:13
5

Try with:

   seq [OPTION]... FIRST INCREMENT LAST

Example:

$ seq 10 -1 1

jluna
  • 51
4

You can reverse the order using tac (cat in reverse). Even if seq should behave differently on various system, I think the following should be as portable as possible:

$ seq 1 10 | tr '\012' ',' | sed 's/,$//'; echo
1,2,3,4,5,6,7,8,9,10
$ seq 1 10 | tac | tr '\012' ',' | sed 's/,$//'; echo
10,9,8,7,6,5,4,3,2,1
$
hlovdal
  • 661
1

I often loop over months and find the -w option useful (to preserve the two digits).

for i in $(seq -w 12 -1 01); do echo $i; done

I’m not sure why the -s, was used in the original reply

Miles
  • 13
0
for i in `seq 1 20 | sort -nr`; do echo $i; done
AdminBee
  • 22,803
Tony Xu
  • 101