Changer l'ordre des lignes dans un fichier


11

J'essaie de changer l'ordre des lignes dans un modèle spécifique. Travailler avec un fichier avec plusieurs lignes (ex. 99 lignes). Pour toutes les trois lignes, je voudrais que la deuxième ligne soit la troisième ligne et la troisième la deuxième ligne.

EXEMPLE.

1- Entrée:

gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...

2- Sortie:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...

Réponses:


12

Utilisation awket mathématiques entières:

awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay } }' /path/to/input

L'opérateur de module effectue une division entière et renvoie le reste, donc pour chaque ligne, il retournera la séquence 1, 2, 0, 1, 2, 0 [...]. Sachant cela, nous enregistrons simplement l'entrée sur des lignes où le module est 2 pour plus tard - à savoir, juste après avoir imprimé l'entrée lorsqu'elle est nulle.


Nous avons un petit défaut ici. Voir ma réponse, partie amélioration mineure
Sergiy Kolodyazhnyy

Merci pour la bonne prise; J'ai intégré un correctif dans ma réponse sous la forme de NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti

23
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

Autrement dit, pimprimez la ligne actuelle, récupérez la ligne next, hancienne, obtenez la ligne next Get la ligne maintenue (ajoutez-la à l'espace de motif) et pimprimez cet espace de motif de 2 lignes avec les troisième et deuxième lignes permutées.


3

Une autre approche awk :

awk '{print $0; if ((getline L2)>0 && (getline L3)>0){ print L3 ORS L2 }}' file

Le résultat:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

  • (getline L2)>0 && (getline L3)>0- extrait les 2 enregistrements suivants s'ils existent

  • chaque 2e et 3e enregistrements sont respectivement affectés à L2et L3variables


1
Je suppose que ces variables commencent par la lettre L (minuscule). Ce sont de mauvais choix pour la lisibilité car ils ressemblent aux chiffres de douze et treize. Un meilleur choix pourrait être line2, etc.
pause jusqu'à nouvel ordre.

@DennisWilliamson, changé en majuscule
RomanPerekhrest

1

Utilisation perlet un court script:

user@pc:~$ cat input.txt 
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

user@pc:~$ perl -ne '$l2=<>; $l3=<>; print $_,$l3,$l2;' input.txt 
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Le script traite l'ensemble du fichier, pour chaque ligne (stockée dans $_), il obtiendra les deux lignes suivantes ( $l2et $l3) et les imprimera dans l'ordre demandé: ligne1, ligne3, ligne2.


1

Une façon pourrait être la suivante:

sed -e '
   /\n/s/\(.*\)\(\n\)\(.*\)/\3\2\1/;//b
   $!N;$q;N;                            # load up the pattern space with 3 lines provided eof not reached
   P;D;                                 # first just print the first line then interchange the two and print them
' yourfile

Alternativement,

perl -ne 'print $_, reverse scalar <>, scalar <>' yourfile

Résultats

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

1

Pourquoi ne pas simplement faire une boucle while? Sous forme développée:

( while read a
  do
    read b
    read c
    echo "$a"
    echo "$c"
    echo "$b"
  done
) < input.txt

En "format simple ligne":

( while read a ; do read b ; read c ; echo "$a" ; echo "$c" ; echo "$b" ; done) < input.txt

Les sorties:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

1

Perl

perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt

L'idée ici est que nous utilisons l'opérateur modulo %avec une $.variable de numéro de ligne , pour déterminer lequel est chaque premier, lequel est chaque seconde et lequel est chaque troisième ligne. Pour chaque 3ème ligne, le reste est 0, tandis que pour chaque 1ère et 2ème ligne, il aura des numéros correspondants.

Tester:

$ cat input.txt                                                                                                          
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt                                    
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Amélioration mineure

L'approche consistant à stocker la deuxième ligne dans une variable présente un défaut. Que faire si la dernière ligne est la "deuxième", c'est-à-dire que pour ce numéro de ligne, le reste est 2? Le code d'origine dans ma réponse et celle de DopeGhoti ne s'imprimera pas My dog is orangesi nous omettons la dernière ligne. La solution pour cela dans les deux cas est d'utiliser un END{}bloc de code, avec la suppression de la variable temporaire après l'impression. En d'autres termes:

$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt

et

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt 

De cette façon, le code fonctionnera pour un nombre arbitraire de lignes dans un fichier, pas seulement celles divisibles par 3.

Correctif supplémentaire pour le problème mentionné dans les commentaires

Dans le cas de awk, si la dernière ligne du fichier produit une sortie de 1 pour $. % 3, le code précédent a un problème de sortie de la nouvelle ligne vide à cause de l'impression inconditionnelle de END{print delay}, car la printfonction mentionnée dans les commentaires ajoute toujours la nouvelle ligne à la variable sur laquelle elle opère. En cas de perlversion, ce problème ne se produit pas, car avec la fonction -nedrapeaux printn'ajoute pas la nouvelle ligne.

Néanmoins, le correctif dans le cas d'awk est de rendre conditionnel, comme mentionné par Dope Ghoti dans les commentaires, est de vérifier la longueur de la variable temporaire. La version Perl du même correctif serait:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 

1
Votre correctif présente un défaut mineur potentiel en ce sens qu'il ajoutera une ligne de sortie vierge pour les fichiers avec le «mauvais» nombre de lignes. J'ai corrigé cela dans mon incorporation de votre amélioration dans ma réponse avec (pour awk) NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti

1
@DopeGhoti Le problème ne se produit pas avec perl, car l'impression de perl avec des -nedrapeaux ne génère pas de nouvelle ligne. Il s'imprime bien, mais c'est une chaîne nulle, pas de retour à la ligne. Néanmoins, j'ai ajouté la mention du problème et le même correctif dans ma réponse. Merci !
Sergiy Kolodyazhnyy

1

Vigueur

Ne convient pas aux fichiers longs, mais toujours pratique si vous venez de modifier un fichier et que vous souhaitez, par exemple, réorganiser certaines strophes Yaml.

Enregistrez d'abord une macro:

gg qq j ddp j q

Et puis répétez le nombre de fois souhaité:

@q @q @q ...

Ou tout simplement par exemple

3@q

Explication:

  • gg - aller à la première ligne
  • qq - démarrer l'enregistrement d'une macro
  • j - aller à la deuxième ligne
  • ddp - échange la deuxième et la troisième ligne
  • j - aller à la quatrième ligne, c'est-à-dire à la première des trois lignes suivantes
  • q - arrêter l'enregistrement
  • @q - rejouer la macro une fois
  • 3 @ q - rejouer la macro trois fois

1
Au lieu de répéter manuellement @q @q @q, il est possible de le faire de cette façon 3@q- répétez trois fois. 100@q- répétez la macro 100 fois.
MiniMax

0

Usage: ./shuffle_lines.awk input.txt

Vérifiez shebang #!/usr/bin/awk -f, car l' awkemplacement peut différer sur votre système.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
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.