Manipulation de texte avec sed

12

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

Existe-t-il un moyen de faire ce qui précède en utilisant sed? Ou dois-je recourir à Python?

Zanna
la source

Réponses:

22

Vous pouvez le faire avec sed, oui, mais d'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 partagera chaque ligne d'entrée sur les espaces (par défaut), l' enregistrement de chaque champs $1, $2, $N. Donc:

  • printf "%s ", $2; imprimera le 2è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 ( NFest le nombre de champs) et pour chacun d'eux il imprimera le 1er champ, a :, puis le champ actuel et a :1.
  • print "" : ceci imprime juste une nouvelle ligne finale.

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

Les -amarques perlse comportent comme awket divisent leur entrée sur les espaces. Ici, les champs sont stockés dans le tableau @F, ce qui signifie que le 1er champ sera $F[0], le 2ème $F[1]etc. Donc:

  • print "$F[1] " : imprime le 2ème champ.
  • print "$F[0]:$_:1 " for @F[2..$#F];: itérer sur les champs 3 jusqu'au dernier champ ( $#Fest le nombre d'éléments dans le tableau @F, donc @F[2..$#F]prend une tranche de tableau commençant au 3e élément jusqu'à la fin du tableau) et imprime le 1er champ, a :, puis le champ actuel et a :1.
  • print "\n" : ceci imprime juste une nouvelle ligne finale.
terdon
la source
12

Voici une horrible sed façon!

$ 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 lisible:

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 utiliser ERE
  • s/old/new/remplacer oldparnew
  • ^([0-9]+) enregistrer quelques chiffres au début de la ligne
  • \1 référence arrière au premier motif enregistré
  • :a étiqueter cette section du script a
  • ( |$) soit un espace ou la fin de la ligne
  • t tester si le dernier remplacement a réussi - si c'est le cas, puis exécutez la commande suivante
  • atrouver l'étiquette :aet recommencer
  • s/ $// supprimer l'espace de fuite

Donc, 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 numéro suivant ...

Mais je suis d'accord que d'autres outils facilitent la tâche ...

Zanna
la source
J'attendais votre solution sed: D
Ravexina
: D il m'a fallu un certain temps @Ravexina - Je pense que muru peut en faire un plus propre
Zanna
5

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

Production:

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 
Cyrus
la source
5

Eh bien, vous pouvez le faire dans sed, mais python fonctionne aussi.

$ ./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.pysont les suivants:

#!/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 particulièrement spécial. Nous ouvrons le premier argument de ligne de commande en tant que fichier à lire et procédons à la décomposition de chaque ligne en "mots" ou éléments individuels. Les premiers mots deviennent prefvariables, et nous imprimons sur le second élément (mots [1]) standard se terminant par un espace. Ensuite, nous construisons un nouvel ensemble de «mots» via des listes de compréhension et .join()fonctionnons sur une liste temporaire de pref, chaque mot et chaîne "1". La dernière étape consiste à les imprimer

Sergiy Kolodyazhnyy
la source
4

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) itère sur le 3ème à l'avant-dernier champs et imprime les champs au format souhaité (premier champ, puis deux points, puis le champ courant, puis deux points, enfin 1) avec un espace de fin

  • printf("%s:%s:1\n", $1, $NF) imprime le dernier champ avec la 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
heemayl
la source