web-dev-qa-db-fra.com

Manipulation de texte avec sed

Actuellement, j'ai plusieurs fichiers texte dont le contenu ressemble à ceci (avec plusieurs lignes):

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

Je souhaite changer chaque ligne pour avoir le format suivant:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Y at-il un moyen de faire ce qui précède en utilisant sed? Ou dois-je avoir recours à Python?

12
user695634

Vous pouvez le faire avec sed, oui, mais les autres outils sont plus simples. Par exemple:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Explication

awk scinde chaque ligne d’entrée sur des espaces (par défaut), en enregistrant chaque champ sous le nom $1, $2, $N. Alors:

  • printf "%s ", $2; imprimera le deuxième champ et un espace de fin.
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: itérera sur les champs 3 jusqu'au dernier champ (NF est le nombre de champs) et pour chacun d'eux il imprimera le 1er champ, un :, puis le champ actuel et un :1.
  • print "": ceci n'imprime qu'une dernière ligne.

Ou Perl:

$ Perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Explication

Le -a fait en sorte que Perl se comporte comme awk et divise son entrée en espaces. Ici, les champs sont stockés dans le tableau @F, ce qui signifie que le premier champ sera $F[0], le second $F[1], etc. Ainsi:

  • print "$F[1] ": affiche le deuxième champ.
  • print "$F[0]:$_:1 " for @F[2..$#F];: itérer sur les champs 3 jusqu'au dernier champ ($#F est le nombre d'éléments dans le tableau @F, afin que @F[2..$#F] prenne une tranche de tableau commençant au 3ème élément jusqu'à la fin du tableau) et affiche le premier champ, un :, puis le champ actuel et un :1.
  • print "\n": ceci n'imprime qu'une dernière ligne.
22
terdon

Voici une horrible sedway!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Plus lisiblement:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

Remarques

  • -r utilisez ERE
  • s/old/new/ remplace oldpar newname__
  • ^([0-9]+) enregistrer des nombres au début de la ligne
  • \1 backreference au premier motif enregistré
  • :a étiquette cette section du script aname__
  • ( |$) soit un espace ou la fin de la ligne
  • tvérifier si le dernier remplacement a réussi. Si c'est le cas, exécutez la commande suivante.
  • atrouver l'étiquette :a et recommencez
  • s/ $// supprime l'espace de fin

Ainsi, après avoir ajouté la structure à la première partie, nous trouvons à plusieurs reprises la dernière instance de la structure et l'appliquons au nombre suivant ...

Mais je suis d'accord que d'autres outils facilitent les choses ...

12
Zanna

Avec awk:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

ou avec bash:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

Sortie:

 0 565: 10: 1 565: 12: 1 565: 23: 1 565: 18: 1 565: 17: 1 565: 25: 1 
 1 564: 7: 1 564: 12 : 1 564: 13: 1 564: 16: 1 564: 18: 1 564: 40: 1 564: 29: 1 564: 15: 1 
5
Cyrus

Eh bien, vous pouvez le faire en mode sed, mais python fonctionne également.

$ ./reformatfile.py  input.txt                                                                        
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Le contenu du reformatfile.py est le suivant:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        words = line.strip().split()
        pref = words[0]
        print(words[1],end=" ")
        new_words = [ ":".join([pref,i,"1"]) for i in words[2:] ]
        print(" ".join(new_words))

Comment cela marche-t-il? Il n'y a vraiment rien de spécial en particulier. Nous ouvrons le premier argument de ligne de commande sous forme de fichier pour la lecture, puis décomposons chaque ligne en "mots" ou en éléments individuels. Les premiers mots deviennent la variable pref et nous imprimons sur le deuxième élément de stdout (mots [1]) se terminant par un espace. Ensuite, nous construisons un nouvel ensemble de "mots" via la liste compréhensions et la fonction .join() sur une liste temporaire de pref, chaque mot et la chaîne "1". La dernière étape consiste à les imprimer

5

Avec awk:

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

Il s’agit de formater des champs séparés par des espaces au format souhaité:

  • printf("%s ", $2) imprime le deuxième champ avec un espace de fin

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) effectue une itération sur le troisième au dernier dernier champ et imprime les champs au format souhaité (premier champ, puis deux points, puis le champ actuel, puis deux points, enfin 1) avec un espace de fin

  • printf("%s:%s:1\n", $1, $NF) imprime le dernier champ avec nouvelle ligne

Exemple:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
4
heemayl