Повернення вмісту змінної словами


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:

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

У поточній tac -s" " <<< "$VAR "версії він все ще вставляє провідну порожню лінію, додає пробіл та відмічає символ нового рядка (ніби printf '\n%s ' "$reversed_VAR"замість очікуваного printf '%s\n' "$reversed_VAR")
Stéphane Chazelas

@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"

Або з використанням лише однієї змінної (що може бути самим варом):
Примітка. На це рішення не впливає ні глобалізація, ні IFS.

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

a=" $var"
while [[ $a ]]; do
    printf '<%s>' "${a##* }"
    var="${a% *}"
done

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 

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 не дуже великий.


Однак це не поширюється на змінні, значення яких містить символи підстановки (як VAR='+ * ?'). Використовуйте set -o noglobабо set -fвиправити це. Загалом, корисно враховувати значення $IFSта стан глобалізації при використанні оператора split + glob. Ні, що використовувати цей оператор мало сенсу rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )було б набагато більше сенсу.
Стефан Шазелас


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"щоб запобігти розщепленню самої змінної на окремі слова (що hisi робиться оболонкою, а не 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

@iruvar, не очищувач , який би викликав оператор split + glob оператора, не переконуючись, що він $IFSмістить належні символи та не відключаючи частину glob.
Стефан Шазелас

@ StéphaneChazelas, правда - частина пітона все ще чистіша (IMHO). Отже, повертаючись до "$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міститься (пробіл, вкладка, нова лінія, нуль за замовчуванням), або призначте простір $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

Неелегантний програмний інструмент POSIX однолінійний :

echo `echo $VAR | tr ' ' '\n' | sort -nr`
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.