Ответы:
Используя комбинацию tr
+ tac
+paste
$ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
arg5.arg4.arg3.arg2.arg1
Если вы все еще предпочитаете bash, вы можете сделать это следующим образом,
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
Если вы не возражаете против чуть-чуть 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()
соединяет компоненты обратного списка с помощью .
.Вы можете сделать это с помощью одного 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/'
С 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 о rderj:.:
: Присоединиться на .
.@
: 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
(обратите внимание, что zsh
(в sh
эмуляции) yash
и некоторые pdksh
оболочки на основе не являются POSIX в этом отношении в том смысле, что они рассматриваются $IFS
как разделитель полей вместо разделителя полей)
Отличие от некоторых других решений, приведенных здесь, заключается в том, что он не обрабатывает символ новой строки (и, в случае zsh
NUL), в частности, то есть $'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}"
?
for i; do
не POSIX (пока), даже если он поддерживается всеми известными мне оболочками POSIX). Просто это решение является прямым переводом zsh
(разделение на массив, обратный массив, объединение элементов массива), но с использованием операторов только POSIX. (Я изменил , string=$string$IFS; set -- $string
чтобы , set -- $string$IFS
как кажется, работают последовательно через оболочки, спасибо).
Я вижу, что Рахул уже опубликовал собственное решение для 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"
)
но я не мог на этом остановиться и почувствовал необходимость написать рекурсивное решение, ориентированное на bash:
function reversedots2() {
if [[ $1 =~ ^([^.]*)\.(.*)$ ]]
then
printf %s $(reversedots2 "${BASH_REMATCH[2]}") . "${BASH_REMATCH[1]}"
else
printf %s "$1"
fi
}