Реверсирование содержимого переменной словами

13

Так что, если у меня есть переменная

VAR='10 20 30 40 50 60 70 80 90 100'

и повторить это

echo "$VAR"
10 20 30 40 50 60 70 80 90 100

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

echo "$VAR" | <code to reverse it>
100 90 80 70 60 50 40 30 20 10

Я попытался использовать Rev, и он буквально перевернул все, так что получилось

echo "$VAR" | rev
001 09 08 07 06 05 04 03 02 01
Алистер Харди
источник
2
Вы можете найти несколько полезных советов в ответах на этот похожий вопрос: Как читать IP-адрес в обратном направлении? (просто нужно настроить для разделителей пространства вместо периодов)
SteelDriver

Ответы:

21

В системах GNU обратная сторона каталогов:

$ tac -s" " <<< "$VAR "            # Please note the added final space.
100 90 80 70 60 50 40 30 20 10
Ипор Сирсер
источник
Ура! Это добилось цели. У меня еще есть чему поучиться
Алистер Харди
@JhonKugelman Без эха, вывод имеет дополнительную новую echo $'100 \n90 80 70 60 50 40 30 20 10'
строку
В текущей tac -s" " <<< "$VAR "версии он по-прежнему вставляет начальную пустую строку, добавляет завершающий пробел и пропускает завершающий символ новой строки (как будто printf '\n%s ' "$reversed_VAR"вместо ожидаемого printf '%s\n' "$reversed_VAR")
Стефан Шазелас
@don_crissti Оригинальный ответ: echo $(tac -s" " <<< $VAR)не имеет завершающего пробела, потому что $(…)удаляет завершающие символы новой строки и пробелы, если IFS их имеет. Мое решение будет исправлено в ближайшее время, спасибо.
соронтар
6

Для краткого списка и для переменной самой оболочкой является самое быстрое решение:

var="10 20 30 40 50 60 70 80 90 100"
set -- $var; unset a 
for ((i=$#;i>0;i--)); do
    printf '%s%s' "${a:+ }" "${!i}"
    a=1
done
echo

Выход:

100 90 80 70 60 50 40 30 20 10 

Примечание: операция разбиения в set -- $varбезопасна для точной строки, используемой здесь, но не будет таковой, если строка содержит символы подстановки ( *, ?или []). Этого можно избежать set -fдо раскола, если это необходимо. То же самое примечание также действительно для следующих решений (кроме случаев, где указано).

Или, если вы хотите установить другую переменную с обратным списком:

var="10 20 30 40 50 60 70 80 90 100"
set -- $var
for i; do out="$i${out:+ }$out"; done
echo "$out"

Или используя только позиционные параметры.

var="10 20 30 40 50 60 70 80 90 100"
set -- $var
a=''
for    i
do     set -- "$i${a:+ $@}"
       a=1
done
echo "$1"

Или используя только одну переменную (которая может быть самой переменной var):
Примечание. Это решение не зависит ни от глобализации, ни от IFS.

var="10 20 30 40 50 60 70 80 90 100"

a=" $var"
while [[ $a ]]; do
    printf '<%s>' "${a##* }"
    var="${a% *}"
done
sorontar
источник
2

awk для спасения

$ var='10 20 30 40 50 60 70 80 90 100'

$ echo "$var" | awk '{for(i=NF; i>0; i--) printf i==1 ? $i"\n" : $i" "}'
100 90 80 70 60 50 40 30 20 10


с perl, любезно Как прочитать IP-адрес в обратном направлении? опубликовано @steeldriver

$ echo "$var" | perl -lane '$,=" "; print reverse @F'
100 90 80 70 60 50 40 30 20 10


Или с bashсамим собой, преобразовав строку в массив

$ arr=($var)    
$ for ((i=${#arr[@]}-1; i>=0; i--)); do printf "${arr[i]} "; done; echo
100 90 80 70 60 50 40 30 20 10 
Sundeep
источник
2

Вы можете использовать функции bash, такие как массивы, чтобы сделать это полностью в процессе:

#!/bin/bash
VAR="10 20 30 40 50 60 70 80 90 100"
a=($VAR); rev_a=()
for ((i=${#a[@]}-1;i>=0;i--)); do rev_a+=( ${a[$i]} ); done; 
RVAR=${rev_a[*]}; 

Это должен быть самый быстрый способ сделать это при условии, что $ VAR не очень большой.

PSkocik
источник
Это, однако, не распространяется на переменные, значение которых содержит символы подстановки (например VAR='+ * ?'). Используйте set -o noglobили, set -fчтобы исправить это. В целом, $IFSпри использовании оператора split + glob рекомендуется учитывать значение и состояние глобирования. Нет, в этом нет смысла использовать этот оператор rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )будет гораздо больше смысла.
Стефан Шазелас
1
printf "%s\n" $var | tac | paste -sd' '
100 90 80 70 60 50 40 30 20 10
Костас
источник
1

Используйте немного магии Python здесь:

bash-4.3$ VAR='10 20 30 40 50 60 70 80 90 100'
bash-4.3$ python -c 'import sys;print " ".join(sys.argv[1].split()[::-1])'  "$VAR" 
100 90 80 70 60 50 40 30 20 10

Как это работает, довольно просто:

  • мы называем "$VAR"второй параметр командной строки sys.argv[1](первый параметр с -cпараметром всегда просто-c самим собой. Обратите внимание на "$VAR"кавычки, чтобы не разбить саму переменную на отдельные слова (что было сделано с помощью shell, а не python)
  • Далее мы используем .split() метод, чтобы разбить его на список строк
  • [::-1] часть просто расширенный синтаксис среза, чтобы получить обратный список
  • наконец, мы объединяем все вместе обратно в одну строку " ".join()и распечатываем

Но те, кто не являются большими поклонниками Python, мы могли AWKбы сделать по существу одно и то же: разделить и напечатать массив в обратном порядке:

 echo "$VAR" | awk '{split($0,a);x=length(a);while(x>-1){printf "%s ",a[x];x--};print ""}'
100 90 80 70 60 50 40 30 20 10 
Сергей Колодяжный
источник
1
или несколько чищеpython3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
ирувар
@iruvar, а не очиститель , который вызвал бы оператор split + glob оболочки, не убедившись, что $IFSсодержит правильные символы, и не отключая часть glob.
Стефан Шазелас
@ StéphaneChazelas, правда, часть Python все еще чище (ИМХО). Итак, возвращаясь к "$VAR"урожайностиpython3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
iruvar
1

zsh:

print -r -- ${(Oa)=VAR}

$=VARрасщепляется $VARна $IFS. (Oa)упорядочивает результирующий список в обратном порядке массива. print -r --(как в ksh), так же, как echo -E -( zshконкретные) это надежные версииecho : печатает свои аргументы как-разделенные пробелом, завершается символом новой строки.

Если вы хотите разделить только на пробел, а не на то, что $IFSсодержится (пробел, табуляция, новая строка, nul по умолчанию), либо назначьте пробел $IFS, либо используйте явное разбиение, например:

print -r -- ${(Oas: :)VAR}

Для сортировки в обратном порядке:

$ VAR='50 10 20 90 100 30 60 40 70 80'
$ print -r -- ${(nOn)=VAR}
100 90 80 70 60 50 40 30 20 10

POSIXly (так будет работать и с bash):

printfТолько со встроенными в оболочку (за исключением некоторых оболочек) механизмами (лучше для переменных с коротким значением):

unset -v IFS # restore IFS to its default value of spc, tab, nl
set -o noglob # disable glob
set -- $VAR # use the split+glob operator to assign the words to $1, $2...

reversed_VAR= sep=
for i do
  reversed_VAR=$i$sep$reversed_VAR
  sep=' '
done
printf '%s\n' "$reversed_VAR"

С awk(лучше для больших переменных, особенно с bash, но с ограничением размера аргументов (или одного аргумента)):

 awk '
   BEGIN {
     n = split(ARGV[1], a);
     while (n) {printf "%s", sep a[n--]; sep = " "}
     print ""
   }' "$VAR"
Стефан Шазелас
источник
0

Inelegant POSIX программные средства однострочные:

echo `echo $VAR | tr ' ' '\n' | sort -nr`
АРУ
источник