Ordem reversa de elementos delimitados por ponto em uma string

13

Eu tenho uma string de entrada como:

arg1.arg2.arg3.arg4.arg5

A saída que quero é:

arg5.arg4.arg3.arg2.arg1

Nem sempre são 5 arg's, podem ser de 2 a 10.

Como posso fazer isso em um script bash?

    
por Jens 09.08.2016 / 14:14

8 respostas

20
  • 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ê poderia fazer desta 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
    
por 09.08.2016 / 14:17
10

Se você não se importa com um pouco 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 separadas, [::-1] inverte a lista e ".".join() une os componentes da lista invertida com . .
por 09.08.2016 / 15:04
5

Você pode fazer isso com uma única invocação sed :

sed -E 'H;g;:t;s/(.*)(\n)(.*)(\.)(.*)//;tt;s/\.(.*)\n/./'

isso usa o buffer de espera para obter uma nova linha no espaço padrão e usa-o para fazer permutações até que a nova linha não seja mais seguida por nenhum ponto, removendo o ponto inicial do espaço padrão e substituindo a nova linha por um ponto.
Com o BRE e um regex ligeiramente diferente:

sed 'H
g
:t
s/\(.*\)\(\n\)\(.*\)\(\.\)\(.*\)//
tt
s/\(\.\)\(.*\)\n//'

Se a entrada consistir em mais de uma linha e você quiser inverter a ordem em cada linha:

sed -E 'G;:t;s/(.*)(\.)(.*)(\n)(.*)//;tt;s/(.*)\n(\.)(.*)//'
    
por 09.08.2016 / 14:56
3

Solução SED:

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

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:.: : dividido em .
  • Oa : sort array no reverso a rice o rder
  • j:.: : junte-se a . .
  • @ : do "$@" -like expansion quando estiver entre aspas duplas, portanto a expansão não sofrerá remoção vazia.

O equivalente POSIX (ou bash ) 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 emulation), yash e alguns pdksh -shells baseados não são POSIX a esse respeito, pois tratam $IFS como um separador de campo em vez de um delimitador de campo)

Onde ele difere de algumas das outras soluções dadas aqui é que ele não trata o caractere de nova linha (e, no caso de zsh , o NUL também), especialmente, que é $'ab.cd\nef.gh' seria revertido para $'gh.cd\nef.ab' , não $'cd.ab\ngh.ef' ou $'gh.ef.cd.ab' .

    
por 09.08.2016 / 15:15
2

Eu vejo o Rahul postou uma solução bash nativa (entre outras), que é uma versão mais curta do primeiro eu inventei:

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 eu não pude parar por aí e senti a necessidade de escrever uma solução recursiva centrada no bash:

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

Não encontrou uma resposta awk , 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"}'
    
por 10.08.2016 / 12:03
1

Pure Bash, requer período à direita na entrada:

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

Use printf %s "$g" se algum dos elementos pontilhados puder começar com um hífen.

    
por 10.08.2016 / 09:52