Ordine inverso degli elementi delimitati da punti in una stringa


14

Ho una stringa di input come:

arg1.arg2.arg3.arg4.arg5

L'output che voglio è:

arg5.arg4.arg3.arg2.arg1

Non è sempre 5 arg, potrebbe essere da 2 a 10.

Come posso farlo in uno script bash?

Risposte:


22
  • Usando la combinazione di tr+ tac+paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • Se preferisci ancora bash, potresti farlo in questo modo,

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

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

2
Bah, stavo per pubblicare la soluzione Perl :)
ilkkachu,

10

Se non ti dispiace un po 'di python:

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

Esempio:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")genera un elenco contenente .sottostringhe separate, [::-1]inverte l'elenco e ".".join()unisce i componenti dell'elenco invertito ..

5

Puoi farlo con una sola sedchiamata:

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

questo utilizza il buffer di mantenimento per ottenere una nuova riga iniziale nello spazio del modello e lo utilizza per eseguire permutazioni fino a quando la nuova riga non viene più seguita da alcun punto in corrispondenza del quale rimuove il punto iniziale dallo spazio del modello e sostituisce la nuova riga con un punto.
Con BRE e una regex leggermente diversa:

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

Se l'input è composto da più di una riga e si desidera invertire l'ordine su ciascuna riga:

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

3

Soluzione SED:

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

3

Con zsh:

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

Per conservare gli elementi vuoti:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.:: dividersi .
  • Oa: Sorta di matrice in retromarcia un RRay indice o rder
  • j:.:: unisciti ..
  • @: fare "$@"un'espansione simile a virgolette tra virgolette quindi l'espansione non subisce la rimozione vuota.

L' bashequivalente POSIX (o ) potrebbe essere qualcosa del tipo:

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

(si noti che zsh(in shemulazione), yashe alcune pdkshshell basate su di esse non sono POSIX a tale riguardo in quanto trattano $IFScome un separatore di campo anziché come delimitatore di campo)

Il punto in cui differisce da alcune delle altre soluzioni fornite qui è che non tratta il carattere newline (e nel caso di zshquello NUL) in particolare, che $'ab.cd\nef.gh'sarebbe invertito $'gh.cd\nef.ab', no $'cd.ab\ngh.ef'o $'gh.ef.cd.ab'.


Cosa c'è che non va IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"?

@BinaryZebra Niente (a parte forse quello for i; doche non è POSIX (ancora) anche se è supportato da tutte le shell POSIX che conosco). È solo che quella soluzione è una traduzione diretta di zshquella (suddivisa in array, array inverso, unisci elementi array) ma utilizzando operatori solo POSIX. (Ho cambiato il in string=$string$IFS; set -- $stringin set -- $string$IFSquanto sembra funzionare in modo coerente tra le shell, grazie).
Stéphane Chazelas,

2

Vedo che Rahul ha già pubblicato una soluzione bash nativa (tra le altre), che è una versione più breve della prima che ho trovato:

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

ma non potevo fermarmi qui e sentivo il bisogno di scrivere una soluzione ricorsiva bash-centrica:

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

2

Non ho visto una awkrisposta, quindi eccone una:

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

1

Pure Bash, richiede un periodo finale sull'input:

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

Utilizzare printf %s "$g"se uno qualsiasi degli elementi punteggiati potrebbe iniziare con un trattino.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.