Зворотний порядок розділених крапкою елементів у рядку


14

У мене є рядок введення:

arg1.arg2.arg3.arg4.arg5

Я хочу отримати:

arg5.arg4.arg3.arg2.arg1

Це не завжди 5 аргументів, може бути від 2 до 10.

Як я можу це зробити в баш-скрипті?

Відповіді:


22
  • Використання комбінації tr+ tac+paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • Якщо ви все ще віддаєте перевагу баш, ви могли б зробити так,

    IFS=. read -ra line <<< "arg1.arg2.arg3.arg4."
    let x=${#line[@]}-1; 
    while [ "$x" -ge 0 ]; do 
          echo -n "${line[$x]}."; 
          let x--; 
    done
  • Використовуючи perl,

    $ echo 'arg1.arg2.arg3.arg4.arg5' | perl -lne 'print join ".", reverse split/\./;'
    arg5.arg4.arg3.arg2.arg1

2
Ба, я просто збирався розмістити рішення Perl :)
ilkkachu

10

Якщо ви не заперечуєте проти крихітки python:

".".join(s.split(".")[::-1])

Приклад:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")генерує список, що містить .відокремлені підрядки, [::-1]реверсує список та ".".join()з'єднує компоненти перевернутого списку з ..

5

Ви можете це зробити одним sedвикликом:

sed -E 'H;g;:t;s/(.*)(\n)(.*)(\.)(.*)/\1\4\5\2\3/;tt;s/\.(.*)\n/\1./'

при цьому використовується буфер утримування, щоб отримати провідний новий рядок у просторі шаблону, і використовує його для того, щоб робити перестановки, поки нова лінія не перестане супроводжуватися будь-якою точкою, в яку точку вона видаляє провідну крапку з простору шаблону і замінює нову рядок крапкою.
З BRE та дещо іншим регулярним виразом:

sed 'H
g
:t
s/\(.*\)\(\n\)\(.*\)\(\.\)\(.*\)/\1\4\5\2\3/
tt
s/\(\.\)\(.*\)\n/\2\1/'

Якщо вхід складається з декількох рядків і ви хочете змінити порядок у кожному рядку:

sed -E 'G;:t;s/(.*)(\.)(.*)(\n)(.*)/\1\4\5\2\3/;tt;s/(.*)\n(\.)(.*)/\3\2\1/'

3

Розчин СЕД:

sed 's/\./\n/g' yourFile | tac | sed ':a; $!{N;ba};s/\n/./g'

3

З zsh:

$ string=arg1.arg2.arg3.arg4.arg5
$ printf '%s\n' ${(j:.:)${(Oas:.:)string}}
arg5.arg4.arg3.arg2.arg1

Щоб зберегти порожні елементи:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.:: розділити на .
  • Oa: Сортувати масив в зворотному rray Indice про rder
  • j:.:: приєднуйся ..
  • @: do "$@"-like розширення, коли в подвійних лапках, так що розширення не підлягає порожньому видаленню.

POSIX (або bash) еквівалент може бути приблизно таким:

string=arg1.arg2.arg3.arg4..arg5.

IFS=.; set -f
set -- $string$IFS # split string into "$@" array
unset pass
for i do # reverse "$@" array
  set -- "$i" ${pass+"$@"}
  pass=1
done
printf '%s\n' "$*" # "$*" to join the array elements with the first
                   # character of $IFS

(зауважте, що zshshемуляції) yashі деякі pdkshоболонки на основі не є POSIX в цьому плані, оскільки вони розглядаються $IFSяк роздільник поля замість роздільника поля)

Там, де він відрізняється від деяких інших рішень, наведених тут, - це те, що він не розглядає символ нового рядка (а у випадку з zshNUL) також спеціально, до якого $'ab.cd\nef.gh'буде повернено $'gh.cd\nef.ab', $'cd.ab\ngh.ef'чи не $'gh.ef.cd.ab'.


Що не так IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"?

@BinaryZebra Нічого (крім, можливо, того, for i; doщо це не POSIX (поки що), навіть якщо він підтримується всіма оболонками POSIX, які я знаю). Просто це рішення є прямим перекладом zshодного (розбиття на масив, зворотний масив, об'єднання елементів масиву), але з використанням операторів, призначених лише для POSIX. (Я змінив string=$string$IFS; set -- $stringна set -- $string$IFSтак, що, здається, працює постійно в оболонках, дякую).
Стефан Шазелас

2

Я бачу, що Рахул вже розмістив нативне рішення bash (серед інших), що є коротшою версією першого, який я придумав:

function reversedots1() (
  IFS=. read -a array <<<"$1"
  new=${array[-1]}
  for((i=${#array[*]} - 2; i >= 0; i--))
  do
    new=${new}.${array[i]}
  done
  printf '%s\n' "$new"
)

але я не міг зупинитися на цьому і відчув необхідність написати рекурсивне башцентричне рішення:

function reversedots2() {
  if [[ $1 =~ ^([^.]*)\.(.*)$ ]]
  then
    printf %s $(reversedots2 "${BASH_REMATCH[2]}") . "${BASH_REMATCH[1]}"
  else
    printf %s "$1"
  fi
}

2

Не бачив awkвідповіді, ось ось одна:

 echo 'arg1.arg2.arg3' | awk -F. '{for (i=NF; i>0; --i) printf "%s%s", (i<NF ? "." : ""), $i; printf "\n"}'

1

Pure Bash, вимагає проміжного періоду на вході:

echo 'foo.bar.baz.' | ( while read -d . f;do g="$f${g+.}$g" ;done;echo "$g" )

Використовуйте, printf %s "$g"якщо будь-який із пунктирних елементів може починатися з дефісу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.