Odwróć kolejność elementów rozdzielanych kropkami w ciągu


14

Mam ciąg wejściowy, taki jak:

arg1.arg2.arg3.arg4.arg5

Dane wyjściowe, których chcę, to:

arg5.arg4.arg3.arg2.arg1

Nie zawsze jest to 5 argumentów, może wynosić od 2 do 10.

Jak mogę to zrobić w skrypcie bash?

Odpowiedzi:


22
  • Używanie kombinacji tr+ tac+paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • Jeśli nadal wolisz bash, możesz to zrobić w ten sposób,

    IFS=. read -ra line <<< "arg1.arg2.arg3.arg4."
    let x=${#line[@]}-1; 
    while [ "$x" -ge 0 ]; do 
          echo -n "${line[$x]}."; 
          let x--; 
    done
  • Za pomocą perl,

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

2
Bah, właśnie zamierzałem opublikować rozwiązanie Perla :)
ilkkachu

10

Jeśli nie masz nic przeciwko python:

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

Przykład:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")generuje listę zawierającą .oddzielne podciągi, [::-1]odwraca listę i ".".join()łączy elementy odwróconej listy za pomocą ..

5

Możesz to zrobić za pomocą jednego sedwywołania:

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

używa to bufora wstrzymania, aby uzyskać wiodącą nową linię w przestrzeni wzorów i używa jej do przeprowadzania permutacji, dopóki po nowej linii nie będzie już żadnej kropki, w której to punkcie usuwa wiodącą kropkę z przestrzeni wzorów i zastępuje nową linię kropką.
Z BRE i nieco innym wyrażeniem regularnym:

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

Jeśli dane wejściowe składają się z więcej niż jednej linii i chcesz odwrócić kolejność w każdej linii:

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

3

Rozwiązanie SED:

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

3

Z zsh:

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

Aby zachować puste elementy:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.:: podzielone na .
  • Oa: Tablica rodzaj Reverse rray indice O rDer
  • j:.:: dołącz ..
  • @: zrób "$@"podobne do rozwinięcia w podwójnych cudzysłowach, aby rozszerzenie nie zostało usunięte.

Odpowiednikiem POSIX (lub bash) może być coś takiego:

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

(zauważ, że zsh(w shemulacji) yashi niektórepdksh powłoki oparte na tym systemie nie są pod tym względem POSIX-owe, ponieważ traktują one $IFSjako separator pól zamiast separatora pól)

Różni się od innych podanych tu rozwiązań tym, że nie traktuje specjalnie znaku nowej linii (aw przypadku zshNUL) specjalnie, to znaczy, że $'ab.cd\nef.gh'byłby odwrócony na $'gh.cd\nef.ab', nie $'cd.ab\ngh.ef'lub $'gh.ef.cd.ab'.


Co jest nie tak IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"?

@BinaryZebra Nic (może poza tym, for i; doco nie jest POSIX (jeszcze), nawet jeśli jest obsługiwane przez wszystkie znane mi powłoki POSIX). Tyle tylko, że to rozwiązanie jest bezpośrednim tłumaczeniem tego zsh(podzielone na tablicę, tablicę odwrotną, połącz elementy tablicy), ale przy użyciu operatorów tylko POSIX. (Mam zmienił string=$string$IFS; set -- $stringsię set -- $string$IFStak, że wydaje się działać konsekwentnie w całej muszli, dzięki).
Stéphane Chazelas,

2

Widzę, że Rahul opublikował już natywne rozwiązanie bash (między innymi), które jest krótszą wersją pierwszego, który wymyśliłem:

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

ale nie mogłem się na tym zatrzymać i poczułem potrzebę napisania rekurencyjnego rozwiązania zorientowanego na bash:

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

2

Nie widziałem awkodpowiedzi, więc oto jedna:

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

1

Pure Bash, wymaga okresu końcowego na wejściu:

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

Użyj, printf %s "$g"jeśli dowolny z kropkowanych elementów może zaczynać się od myślnika.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.