Comment sélectionner certaines lignes (n, n + 4, n + 8, n + 12…) dans le fichier?


Réponses:


28

Utilisation d'AWK:

awk '!((NR - 1) % 4)' input > output

Comprendre comment cela fonctionne est laissé comme un exercice pour le lecteur.


merci pour ce court cours awk!
darxmurf

20
NR % 4 == 1serait l'OMI plus lisible.
Stéphane Chazelas

12
D'accord @ Stéphane; cela est probablement douteux de ma part, mais pour des questions potentiellement à faire mes devoirs, j'essaye de brouiller un peu mes réponses ...
Stephen Kitt

@StephenKitt cache vos réponses? Vraiment? Ce n'est pas l'endroit pour ça.
données du

22

Utilisation de split (GNU coreutils):

split -nr/1/4 input > output
  • -ngénérer CHUNKSdes fichiers de sortie

et CHUNKScomme

  • r/K/N utiliser la distribution à tour de rôle et ne sortir que Kth de N vers sortie standard sans séparer les lignes / enregistrements

1
L'esprit soufflé. Des réponses comme celle-ci expliquent pourquoi j'adore ce SE. Merci!
user1717828

21

Avec GNU sed:

sed '1~4!d' < input > output

Avec standard sed:

sed -n 'p;n;n;n' < input > output

Avec 1et 4dans $net $ivariables:

sed "$n~$i!d" # GNU only
awk -v n="$n" -v i="$i" 'NR >= n && (NR % i) == (n % i)'

7

Ajout de la solution Perl obligatoire:

perl -ne 'print if $. % 4 == 1' input > output

4

Version Python, juste pour le plaisir:

with open('input.txt') as f:
    for i, line in enumerate(f.readlines()):
        if i%4 == 0:
            print(line.strip())

enumerate(f)devrait être en mesure de faire le travail tout en consommant moins de mémoire
iruvar

@iruvar C'est tellement bien! Jamais réalisé cela auparavant; utilisera à l'avenir. N'hésitez pas à le modifier dans cette réponse; Je ne vais pas vraiment le maintenir avec des optimisations car les autres réponses Bash (en particulier celle-ci ) sont définitivement la voie à suivre.
user1717828

Si vous allez utiliser readlines(d'où la fusion du fichier entier en mémoire), vous pouvez utiliser f.readlines()[::4]pour obtenir toutes les quatre lignes. Vous pouvez donc utiliser print(''.join(f.readlines()[::4])).
Nick Matteo

3

POSIX sed: cette méthode utilise le posixly sed et peut donc être exécutée partout, ou au moins les seds qui respectent le posix.

 $ sed -ne '
   /\n/!{
    H;s/.*//;x
   }

   :loop
       $bdone
       N;s/\n/&/4
       tdone
   bloop

   :done
   s/.//;P
 ' input.file

Un autre est une génération de code sed programmatique à des fins d'évolutivité:

$ code=$(yes n | head -n 4 | paste -sd\; | sed s/n/p/)
$ sed -ne "$code" input.file

Perl: nous remplissons le tableau A jusqu'à ce qu'il ait une taille de 4. Ensuite, nous imprimons son premier élément et effaçons également le tableau.

$ perl -pe '
   $A[@A] = @A ? <> : $_ while @A < 4;
   $_ = (splice @A)[0];
' input.file

1

Appelez avec scriptname filename skip(4 dans votre cas) Cela fonctionne en tirant des iterlignes du haut du fichier et en ne sortant que le dernier. Il augmente ensuite iterde skipset se répète tant que la valeur de itern'a pas dépassé le linesin file.

#!/bin/bash
file="$1"
lines=`wc -l < "$file"`
skips="$2" || "4"
iter=1
while [ "$iter" -le "$lines" ]; do
 head "$file" -n $iter | tail -n 1
 iter=$(( $iter + $skips ))
done

1

Pure Bash:

mapfile -t lines < input
for (( i=0; i < ${#lines[@]}; i+=4 ))
do printf "%s\n" "${lines[$i]}"
done

mapfile est un module intégré ajouté dans Bash 4 qui lit l'entrée standard dans un tableau, ici nommé lines, avec une ligne par entrée. L' -toption supprime les nouvelles lignes finales.

Si vous souhaitez imprimer toutes les quatre lignes à partir de la ligne 4, vous pouvez le faire dans une seule commande en utilisant mapfilel'option de rappel de -C, qui exécute le code fourni toutes les lignes, avec l'intervalle donné par -c. L'index du tableau actuel et la ligne suivante à affecter sont donnés au code comme arguments.

mapfile -t -c4 -C 'printf "%.0s%s\n"' < input

Cela utilise la fonction printfintégrée; le code de format %.0ssupprime le premier argument (l'index), donc seule la ligne est imprimée.

Vous pouvez utiliser la même commande pour imprimer toutes les quatre lignes à partir de la ligne 1, 2 ou 3, mais vous devez ajouter 3, 2 ou 1 lignes inputavant de l'alimenter mapfile, ce qui, à mon avis, est plus problématique que cela ne vaut la peine .

Cela fonctionne également:

mapfile -t lines < input
printf "%s%.0s%.0s%.0s\n" "${lines[@]}"

Ici, printfconsomme quatre entrées du tableau linesà la fois, imprimant uniquement la première et ignorant les trois autres avec %.0s. Je n'aime pas cela, car vous devez manipuler manuellement la chaîne de format pour différents intervalles ou points de départ.

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.