Ordre inverse des éléments délimités par des points dans une chaîne


14

J'ai une chaîne d'entrée comme:

arg1.arg2.arg3.arg4.arg5

La sortie que je veux est:

arg5.arg4.arg3.arg2.arg1

Ce n'est pas toujours 5 arguments, pourrait être de 2 à 10.

Comment puis-je faire cela dans un script bash?

Réponses:


22
  • Utilisation d'une combinaison de tr+ tac+paste

    $ tr '.' $'\n' <<< 'arg1.arg2.arg3.arg4.arg5' | tac | paste -s -d '.'
    arg5.arg4.arg3.arg2.arg1
  • Si vous préférez toujours bash, vous pouvez le faire de cette façon,

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

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

2
Bah, j'allais juste poster la solution Perl :)
ilkkachu

10

Si cela ne vous dérange pas un petit peu de python:

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

Exemple:

$ python3 -c 's="arg1.arg2.arg3.arg4.arg5"; print(".".join(s.split(".")[::-1]))'
arg5.arg4.arg3.arg2.arg1
  • s.split(".")génère une liste contenant .des sous-chaînes séparées, [::-1]inverse la liste et ".".join()joint les composants de la liste inversée avec ..

5

Vous pouvez le faire avec une seule sedinvocation:

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

cela utilise le tampon de maintien pour obtenir une nouvelle ligne de début dans l'espace de motif et l'utilise pour faire des permutations jusqu'à ce que la nouvelle ligne ne soit plus suivie par aucun point auquel point il supprime le point de tête de l'espace de motif et remplace la nouvelle ligne par un point.
Avec BRE et un regex légèrement différent:

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

Si l'entrée se compose de plusieurs lignes et que vous souhaitez inverser l'ordre sur chaque ligne:

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

3

Solution SED:

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

3

Avec zsh:

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

Pour conserver les éléments vides:

$ string=arg1.arg2.arg3.arg4..arg5.
$ printf '%s\n' ${(j:.:)"${(@Oas:.:)string}"}
.arg5..arg4.arg3.arg2.arg1
  • s:.:: divisé sur .
  • Oa: Tableau de tri en sens inverse un rray indice o rder
  • j:.:: rejoignez-nous ..
  • @: "$@"extension à la manière des guillemets doubles pour que l'expansion ne soit pas supprimée à vide.

L' bashéquivalent POSIX (ou ) pourrait ressembler à:

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

(notez que zsh(en shémulation), yashet certainspdksh shells basés sur ne sont pas POSIX à cet égard en ce qu'ils traitent $IFScomme un séparateur de champ au lieu d'un délimiteur de champ)

Là où il diffère de certaines des autres solutions données ici, c'est qu'il ne traite pas zshspécialement le caractère de nouvelle ligne (et dans le cas de , le NUL), qui $'ab.cd\nef.gh'serait inversé en $'gh.cd\nef.ab', pas $'cd.ab\ngh.ef'ou $'gh.ef.cd.ab'.


Qu'est-ce qui ne va pas IFS="."; set -f; set -- $string$IFS; for i; do rev="$i$IFS$rev"; done; printf '%s\n' "${rev%$IFS}"?

@BinaryZebra Rien (à part peut-être celui for i; doqui n'est pas (encore) POSIX même s'il est supporté par tous les shells POSIX que je connais). C'est juste que cette solution est une traduction directe de zshcelle (divisée en tableau, tableau inversé, joindre des éléments de tableau) mais en utilisant des opérateurs POSIX uniquement. (J'ai changé le string=$string$IFS; set -- $stringen set -- $string$IFScar cela semble fonctionner de manière cohérente entre les shells, merci).
Stéphane Chazelas

2

Je vois que Rahul a déjà posté une solution bash native (entre autres), qui est une version plus courte de la première que j'ai inventée:

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

mais je n'ai pas pu m'arrêter là, et j'ai ressenti le besoin d'écrire une solution récursive centrée sur bash:

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

2

Je n'ai pas vu de awkréponse, alors en voici une:

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

1

Pure Bash, nécessite une période de fin en entrée:

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

À utiliser printf %s "$g"si l'un des éléments en pointillé peut commencer par un trait d'union.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.