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.

Stephen Kitt
la source
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
Freddy
la source
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)'
Stéphane Chazelas
la source
7

Ajout de la solution Perl obligatoire:

perl -ne 'print if $. % 4 == 1' input > output
gémir
la source
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())
user1717828
la source
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
Rakesh Sharma
la source
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
Ryan Grange
la source
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.

Nick Matteo
la source