Изменение содержимого переменной на обратное по словам

Изменение содержимого переменной на обратное по словам

Итак, если у меня есть переменная

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

решение1

В системах GNU обратная команда cat — tac:

$ tac -s" " <<< "$VAR "            # Please note the added final space.
100 90 80 70 60 50 40 30 20 10

решение2

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

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

решение3

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 

решение4

zsh:

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

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

Если вы хотите разделить только по пробелу, а не по тому, что $IFSсодержит (пробел, табуляция, новая строка, ноль по умолчанию), либо назначьте пробел $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"

Связанный контент