Respostas:
Usando a combinação de tr
+ tac
+paste
$ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
arg5.arg4.arg3.arg2.arg1
Se você ainda preferir bash, você pode fazer dessa maneira,
IFS=. read -ra line <<< "arg1.arg2.arg3.arg4."
let x=${#line[@]}-1;
while [ "$x" -ge 0 ]; do
echo -n "${line[$x]}.";
let x--;
done
Usando perl
,
$ echo 'arg1.arg2.arg3.arg4.arg5' | perl -lne 'print join ".", reverse split/\./;'
arg5.arg4.arg3.arg2.arg1
Se você não se importa com um pouquinho de python
:
".".join(s.split(".")[::-1])
Exemplo:
$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
s.split(".")
gera uma lista contendo .
substrings separados, [::-1]
inverte a lista e ".".join()
une os componentes da lista invertida .
.Você pode fazer isso com uma única sed
chamada:
sed -E 'H;g;:t;s/(.*)(\n)(.*)(\.)(.*)/\1\4\5\2\3/;tt;s/\.(.*)\n/\1./'
isso usa o buffer de espera para obter uma nova linha de frente no espaço do padrão e usa-o para fazer permutações até que a nova linha não seja mais seguida por nenhum ponto; nesse ponto, ele remove o ponto principal do espaço do padrão e substitui a nova linha por um ponto.
Com BRE e uma regex ligeiramente diferente:
sed 'H
g
:t
s/\(.*\)\(\n\)\(.*\)\(\.\)\(.*\)/\1\4\5\2\3/
tt
s/\(\.\)\(.*\)\n/\2\1/'
Se a entrada consistir em mais de uma linha e você desejar reverter a ordem em cada linha:
sed -E 'G;:t;s/(.*)(\.)(.*)(\n)(.*)/\1\4\5\2\3/;tt;s/(.*)\n(\.)(.*)/\3\2\1/'
Com zsh
:
$ string=arg1.arg2.arg3.arg4.arg5
$ printf '%s\n' ${(j:.:)${(Oas:.:)string}}
arg5.arg4.arg3.arg2.arg1
Para preservar elementos vazios:
$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
s:.:
: dividir em .
Oa
: Matriz tipo, em sentido inverso um rray indice o rderj:.:
: junte-se .
.@
: "$@"
expansão do tipo quando estiver entre aspas duplas, para que a expansão não seja removida em branco.O bash
equivalente POSIX (ou ) poderia ser algo como:
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
(observe que zsh
(em sh
emulação) yash
e algumas pdksh
conchas baseadas em não são POSIX nesse sentido, pois tratam $IFS
como um separador de campos em vez de delimitador de campos)
Onde difere de algumas das outras soluções apresentadas aqui é que ele não trata o caractere de nova linha (e, no caso de zsh
NUL) especialmente, ou seja, $'ab.cd\nef.gh'
seria revertido para $'gh.cd\nef.ab'
, não $'cd.ab\ngh.ef'
ou $'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
que ainda não é POSIX), mesmo que seja suportado por todos os shells POSIX que eu conheço. Só que essa solução é uma tradução direta de zsh
uma (dividida em matriz, matriz reversa, junção de elementos da matriz), mas usando operadores somente POSIX. (Eu mudei string=$string$IFS; set -- $string
para para, set -- $string$IFS
pois parece funcionar de forma consistente entre conchas, obrigado).
Vejo Rahul já postou uma solução nativa do bash (entre outras), que é uma versão mais curta da primeira que eu criei:
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"
)
mas não consegui parar por aí e senti a necessidade de escrever uma solução centrada no bash recursiva:
function reversedots2() {
if [[ $1 =~ ^([^.]*)\.(.*)$ ]]
then
printf %s $(reversedots2 "${BASH_REMATCH[2]}") . "${BASH_REMATCH[1]}"
else
printf %s "$1"
fi
}
Não encontrou uma awk
resposta, então aqui está uma:
echo 'arg1.arg2.arg3' | awk -F. '{for (i=NF; i>0; --i) printf "%s%s", (i<NF ? "." : ""), $i; printf "\n"}'