Umgekehrte Reihenfolge von durch Punkte getrennten Elementen in einer Zeichenfolge

14

Ich habe eine Eingabezeichenfolge wie:

arg1.arg2.arg3.arg4.arg5

Die Ausgabe, die ich möchte, ist:

arg5.arg4.arg3.arg2.arg1

Es sind nicht immer 5 Argumente, es können 2 bis 10 sein.

Wie kann ich das in einem Bash-Skript machen?

Jens
quelle

Antworten:

22
  • Mit der Kombination von tr+ tac+paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • Wenn Sie immer noch Bash bevorzugen, können Sie auf diese Weise tun,

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

    $ echo 'arg1.arg2.arg3.arg4.arg5' | perl -lne 'print join ".", reverse split/\./;'
    arg5.arg4.arg3.arg2.arg1
Rahul
quelle
2
Bah, ich wollte gerade die Perl-Lösung
posten
10

Wenn es Ihnen nichts ausmacht python:

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

Beispiel:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")generiert eine Liste mit .getrennten Teilzeichenfolgen, [::-1]kehrt die Liste um und ".".join()verbindet die Komponenten der umgekehrten Liste mit ..
heemayl
quelle
5

Sie können dies mit einem einzigen sedAufruf tun :

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

Dies verwendet den Haltepuffer, um eine führende neue Zeile im Musterraum zu erhalten, und verwendet ihn, um Permutationen durchzuführen, bis auf die neue Zeile kein Punkt mehr folgt. An diesem Punkt wird der führende Punkt aus dem Musterraum entfernt und die neue Zeile durch einen Punkt ersetzt.
Mit BRE und einem etwas anderen Regex:

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

Wenn die Eingabe aus mehr als einer Zeile besteht und Sie die Reihenfolge in jeder Zeile umkehren möchten:

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

SED-Lösung:

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

Mit zsh:

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

So bewahren Sie leere Elemente auf:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.:: aufgeteilt auf .
  • Oa: Sort Array in umgekehrter ein rray indice o rder
  • j:.:: mach mit ..
  • @: do- "$@"like-Erweiterung in doppelten Anführungszeichen, damit die Erweiterung nicht entleert wird.

Das POSIX (oder bash) Äquivalent könnte etwa so lauten:

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

(Beachten Sie, dass zsh(in der shEmulation) yashund einige pdksh-basierte Shells in dieser Hinsicht keine POSIX-Shells sind, da sie $IFSals Feldtrennzeichen anstelle eines Feldtrennzeichens behandelt werden. )

Wo es sich von einigen anderen hier angegebenen Lösungen unterscheidet, ist, dass es das Newline-Zeichen (und im Falle des zshNUL- Zeichens auch nicht) nicht speziell behandelt, das heißt, es $'ab.cd\nef.gh'würde umgekehrt werden $'gh.cd\nef.ab', nicht $'cd.ab\ngh.ef'oder $'gh.ef.cd.ab'.

Stéphane Chazelas
quelle
Was ist los mit IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"?
@BinaryZebra Nichts (abgesehen for i; dodavon, dass es sich (noch) nicht um POSIX handelt, selbst wenn es von allen mir bekannten POSIX-Shells unterstützt wird). Es ist nur so, dass diese Lösung eine direkte Übersetzung der zsheinen Lösung ist (aufgeteilt in Array-, Reverse-Array- und Join-Array-Elemente), aber nur mit POSIX-Operatoren. (Ich habe die Einstellung auf geändert string=$string$IFS; set -- $string, set -- $string$IFSda dies über alle Shells hinweg konsistent zu funktionieren scheint, danke).
Stéphane Chazelas
2

Ich sehe, Rahul hat (unter anderem) bereits eine native Bash-Lösung veröffentlicht, die eine kürzere Version der ersten ist, die ich mir ausgedacht habe:

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

Aber ich konnte nicht damit aufhören und hatte das Bedürfnis, eine rekursive bash-zentrierte Lösung zu schreiben:

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

Ich habe keine awkAntwort gefunden. Hier ist eine:

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

Pure Bash, benötigt eine Nachlaufzeit bei der Eingabe:

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

Verwenden Sie printf %s "$g"diese Option, wenn eines der gepunkteten Elemente mit einem Bindestrich beginnen könnte.

Eric
quelle