Как отобразить числа в обратном порядке, используя seq (1)?


36

У меня есть перебора чисел в различном порядке. Я могу отображать их в возрастающем порядке, даже с такими шагами, как:

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

Я также могу отображать их в обратном порядке, ни непрерывно, ни пошагово.

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

Нет вывода для вышеуказанных команд.

Мои детали оболочки:

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

Дайте мне знать, как я смогу отображать числа в порядке убывания?


10
Для будущих читателей seqэто совершенно нестандартный инструмент, и нет никаких гарантий, что любые две реализации будут одинаковыми. Если вам нужно написать цикл, который повторяется в обратном порядке по числам в bash, используйте for ((i=$max;i>=0;i--)) …или тому подобное.
Кодзиро

Ответы:


50

использовать отрицательное приращение

seq -s, 10 -2 1
10,8,6,4,2

20

В общем, вы не хотите использовать seq, это не переносимо (даже среди стандартных сред Linux). Если вы используете ksh, zsh или bash4 +, вы можете использовать скобки:

echo {10..1..2} | tr " " ,
10,8,6,4,2

1
Это коротко и быстро, но я на старой версии Bash.
MTK

20
Хороший ответ, но есть некоторая ирония, когда вы указываете на seqнестандартность, а затем используете расширение скобки только для bash-4. ;)
Кодзиро

@kojiro - Нет аргументов, чтобы быть честным ;-) Моя главная проблема не в том, существует ли команда (это может или не может иметь значение в зависимости от того, распространяется ли сценарий и т. д.), а в том, выполняется ли команда так, как ожидается Автор. Расширение скобок bash4 почти гарантирует это (если оно работает, оно работает, как вы ожидаете), тогда как seqнет.
Крис Даун

1
Почему это не портативный? У вас есть источники или доказательства? Мне это интересно.
Бенуа Даффес

15

Другой способ в чистом bash, ksh или zsh:

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

Чистый способ POSIX:

i=10
while [ "$i" -gt 2 ]; do printf "$i,"; i=$((i-2)); done
echo "$i"

1
forВторое выражение должно быть тестом, а третье - шагом.
manatwork

7

Теперь стандартные POSIX:

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

(интересно, с mawk(и в меньшей степени gawkтакже) намного быстрее, чем seqдля GNU i = 10000000вместо i = 10)

Или

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

(будет эффективнее только с небольшим количеством итераций, особенно с bash)

Или

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

(который поддерживал бы числа любого размера, но учтите, что после определенного количества цифр ( по крайней мере, чисел, превышающих 10 70 в локали POSIX), строки будут заключены в обратную косую черту)


1
В GNU bc вы можете избежать переноса строк, установив BC_LINE_LENGTH=0в среде. Нет такой удачи на других реализациях.
Жиль "ТАК - перестань быть злым"

1
Зачем использовать позиционные аргументы, а не зацикливаться s=$s,$iили вызывать echo -n/ echo \c/ printf?
Жиль "ТАК - перестань быть злым"

2

Вы можете отменить заказ, используя tac(кошка в обратном порядке). Даже если seqв разных системах они должны вести себя по-разному, я думаю, что следующее должно быть максимально переносимым:

$ 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
$

2

Попробуйте с:

   seq [OPTION]... FIRST INCREMENT LAST

Пример:

$ seq 10 -1 1

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.