Respuestas:
Usando la combinación de tr
+ tac
+paste
$ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
arg5.arg4.arg3.arg2.arg1
Si aún prefieres bash, puedes hacerlo de esta manera,
IFS=. read -ra line <<< "arg1.arg2.arg3.arg4."
let x=${#line[@]}-1;
while [ "$x" -ge 0 ]; do
echo -n "${line[$x]}.";
let x--;
done
Utilizando perl
,
$ echo 'arg1.arg2.arg3.arg4.arg5' | perl -lne 'print join ".", reverse split/\./;'
arg5.arg4.arg3.arg2.arg1
Si no le importa un poquito de python
:
".".join(s.split(".")[::-1])
Ejemplo:
$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
s.split(".")
genera una lista que contiene .
subcadenas separadas, [::-1]
invierte la lista y ".".join()
une los componentes de la lista invertida con .
.Puedes hacerlo con una sola sed
invocación:
sed -E 'H;g;:t;s/(.*)(\n)(.*)(\.)(.*)/\1\4\5\2\3/;tt;s/\.(.*)\n/\1./'
esto usa el búfer de retención para obtener una nueva línea inicial en el espacio del patrón y lo usa para realizar permutaciones hasta que la nueva línea ya no esté seguida de ningún punto en el punto en que elimine el punto inicial del espacio del patrón y reemplace la nueva línea con un punto.
Con BRE y una expresión regular ligeramente diferente:
sed 'H
g
:t
s/\(.*\)\(\n\)\(.*\)\(\.\)\(.*\)/\1\4\5\2\3/
tt
s/\(\.\)\(.*\)\n/\2\1/'
Si la entrada consta de más de una línea y desea invertir el orden en cada línea:
sed -E 'G;:t;s/(.*)(\.)(.*)(\n)(.*)/\1\4\5\2\3/;tt;s/(.*)\n(\.)(.*)/\3\2\1/'
Con zsh
:
$ string=arg1.arg2.arg3.arg4.arg5
$ printf '%s\n' ${(j:.:)${(Oas:.:)string}}
arg5.arg4.arg3.arg2.arg1
Para preservar elementos vacíos:
$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
s:.:
: dividido en .
Oa
: Array especie a la inversa un RRay indice O rdenj:.:
: únete .
.@
: hacer una "$@"
expansión similar a comillas dobles para que la expansión no se elimine en vacío.El POSIX (o bash
) equivalente podría 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
(tenga zsh
en cuenta que (en la sh
emulación), yash
y algunos pdksh
shells basados no son POSIX en ese sentido, ya que se tratan $IFS
como un separador de campo en lugar de un delimitador de campo)
Donde difiere de algunas de las otras soluciones dadas aquí es que no trata el carácter de nueva línea (y en el caso del zsh
NUL tampoco) especialmente, eso se $'ab.cd\nef.gh'
revertiría a $'gh.cd\nef.ab'
, no $'cd.ab\ngh.ef'
o $'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 no es POSIX (todavía) incluso si es compatible con todos los shells POSIX que conozco). Es solo que esa solución es una traducción directa de la zsh
misma (dividida en elementos de matriz, matriz inversa, elementos de matriz de combinación) pero utilizando operadores solo POSIX. (He cambiado el string=$string$IFS; set -- $string
a set -- $string$IFS
ya que parece funcionar de manera consistente en todos los shells, gracias).
Veo que Rahul ya publicó una solución bash nativa (entre otras), que es una versión más corta de la primera que se me ocurrió:
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"
)
pero no pude parar allí, y sentí la necesidad de escribir una solución recursiva centrada en bash:
function reversedots2() {
if [[ $1 =~ ^([^.]*)\.(.*)$ ]]
then
printf %s $(reversedots2 "${BASH_REMATCH[2]}") . "${BASH_REMATCH[1]}"
else
printf %s "$1"
fi
}
No vi una awk
respuesta, así que aquí hay una:
echo 'arg1.arg2.arg3' | awk -F. '{for (i=NF; i>0; --i) printf "%s%s", (i<NF ? "." : ""), $i; printf "\n"}'