Comment ne garder que chaque nième ligne d'un fichier


71

J'ai un fichier CSV plutôt volumineux (75 Mo). J'essaie simplement de produire un graphique, donc je n'ai pas vraiment besoin de toutes les données.

Reformulation: j'aimerais supprimer n lignes, puis conserver une ligne, puis supprimer n lignes, etc.

Donc, si le fichier ressemblait à ceci:

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6

et n = 2, alors le résultat serait:

Line 3
Line 6

Il semble que sedje pourrais le faire, mais je n'ai pas réussi à comprendre comment. Une commande bash serait idéale, mais je suis ouvert à toute solution.


2
Voulez-vous vraiment les lignes 1, 3, 6, etc., plutôt que 1, 4, 7, etc.?
Ilmari Karonen

2
Puisqu'il s'agit d'un fichier CSV, je suppose que la première ligne contient des métadonnées (c'est-à-dire les noms de champs). Si tel est le cas, la question devrait être "chaque nième ligne après la première".
iglvzx

7
1, 3, 6 n'a toujours pas de sens!
Wim

1
Je suppose que cela devrait être 1, 3, 5 sauf si n = 2 est une valeur magique pour les nombres triangulaires (1, 3, 6, 10, 15, 21, etc.)
rjmunro

4
Pouvez-vous mettre à jour votre question pour que ce que vous demandez ("chaque nième ligne", "n = 2") et la sortie souhaitée (lignes 3, 6) soient cohérents? Les futurs lecteurs vont être confus.
Keith Thompson

Réponses:


121
~ $ awk 'NR == 1 || NR % 3 == 0' yourfile
Line 1
Line 3
Line 6

NR(nombre d'enregistrements) variable est le nombre d'enregistrements d'enregistrements car le comportement par défaut est nouvelle ligne pour RS(séparateur d'enregistrement). motif et action sont facultatifs dans le format par défaut d'awk 'pattern {actions}'. lorsque nous ne donnons qu'une partie du motif, awktous les champs sont écrits $0pour les trueconditions de notre motif .


8
Grâce aux valeurs par défaut, vous n'avez même pas besoin de beaucoup:awk 'NR == 1 || NR % 3 == 0'
Kevin

@selman: Si vous aimez la solution de Kevin, vous pouvez envisager de mettre à jour votre réponse.
Keith Thompson

4
Voulez-vous expliquer pourquoi il le fait? De cette façon, si quelqu'un veut légèrement le peaufiner, j'espère que votre explication l'aidera à le faire
Ivo Flipse

J'ai trouvé que cette approche me laisse les lignes 1 et 2 intactes. Ceci est confirmé avec pour awk 'NR == 1 || NR % 2 == 0' myfile.txt | wc -lrésultat un nombre impair alors que le fichier original avait un nombre pair de lignes. @kev answer fonctionne mieux dans mon cas de test.
Daniel Da Cunha

58

sed peut aussi faire ceci:

$ sed -n '1p;0~3p' input.txt
Line 1
Line 3
Line 6

man sedexplique ~comme:

première étape Faites correspondre chaque ligne de la ligne en commençant par la première. Par exemple, `` sed -n 1 ~ 2p '' imprimera toutes les lignes impaires du flux d'entrée et l'adresse 2 ~ 5 correspondra toutes les cinq lignes, en commençant par la seconde. le premier peut être zéro; dans ce cas, sed fonctionne comme s'il était égal à step. (Ceci est une extension.)


6
Pourriez-vous expliquer cette commande?
Qed

1
@qed Explication: 1pimprime la première ligne, 0~3pimprime toutes les trois lignes à partir de la ligne 3 (il 1pest donc nécessaire d'imprimer la ligne 1). Mais notez que ce 0~3n’est pas standard mais une extension GNU sed.
Arkku

"Ceci est une extension." Quelle version utilisez / utilisiez-vous?
Victor

Cette réponse m'a beaucoup aidé pour Windows PowerShell. Je l'ai élargi comme ça: sed -n '1p;0~10p' '.\in.txt' > out.txtpour imprimer le fichier réduit dans un fichier de sortie.
Kimliv

22

Perl peut le faire aussi:

while (<>) {
    print  if $. % 3 == 1;
}

Ce programme imprimera la première ligne de son entrée et toutes les trois lignes par la suite.

Pour l'expliquer un peu, <>voici l'opérateur de saisie de ligne, qui itère sur les lignes d'entrée lorsqu'il est utilisé dans une whileboucle comme celle-ci. La variable spéciale $.contient le nombre de lignes lues jusqu'à présent et %constitue l'opérateur de module.

Ce code peut être écrit de manière encore plus compacte en une ligne, en utilisant les commutateurs -net -e:

perl -ne 'print if $. % 3 == 1'  < input.txt  > output.txt

Le -ecommutateur utilise un morceau de code Perl à exécuter en tant que paramètre de ligne de commande, tandis qu'il -nimplicite le code dans une whileboucle similaire à celle présentée ci-dessus.


Edit: Pour obtenir les lignes 1, 3, 6, 9, ... comme dans l'exemple, plutôt que les lignes 1, 4, 7, 10, ... comme je l'avais supposé au départ, remplacez $. % 3 == 1par $. == 1 or $. % 3 == 0.


7

Si vous voulez le faire avec un script Bash , vous pouvez essayer:

#!/bin/sh

echo Please enter the file name
read fname
echo Please enter the Nth lines that you want to keep
read n

exec<$fname
value=0
while read line
do
    if [ $(( $value % $n )) -eq 0 ] ; then
        echo -e "$line" >> new_file.txt
    fi
        let value=value+1 
done
echo "Check the 'new_file.txt' that has been created in this directory";

Enregistrez-le sous le nom "read_lines.sh" et rappelez-vous de donner les autorisations + x au fichier bash.

chmod +x ./read_lines.sh

1
Si vous faites en sorte d'émettre uniquement à la sortie standard, lisez le nombre de lignes à ignorer des arguments et lisez le fichier à partir de l'entrée standard, ce serait plus simple et plus utile. Vous pouvez toujours créer new_file.txt en le faisant ./read_lines.sh > new_file.txt.
rjmunro

4

Une solution en pure bash, qui ne génère pas de processus est la suivante:

{ for f in {1..2}; do read line; done;
  while read line; do
    echo $line;
    for f in {1..2}; do read line; done;
  done; } < file

La première ligne saute 2 lignes au début du fichier, puis whileimprime la ligne suivante et saute à nouveau 2 lignes.

Si votre fichier est petit, c'est un moyen très efficace de faire le travail car il ne démarre pas de processus. Lorsque votre fichier est volumineux, sedutilisez-le, car il est plus efficace que le fichier io bash.


1

Une version Python (Python 2 et Python 3):

python2 -c "print(''.join(open('file.txt').readlines()[::3]))"

remplacez [::3]par les paramètres de début, de fin et de taille de pas pour plus de contrôle. Par exemple, [10:36:5]met les lignes 10,15, ..., 35.

Remarque: étant donné readlines()que les fins de lignes sont conservées, la sortie de cet appel peut se terminer par une dernière ligne vide, à moins que la dernière ligne d'origine ne soit sortie avec la taille de pas choisie.

Une version de flux est également possible (ici, sortie uniquement après flux fini):

python -c "import sys;print(''.join(list(sys.stdin)[::3]))" < file.txt
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.