Come selezionare determinate righe (n, n + 4, n + 8, n + 12 ...) dal file?


Risposte:


28

Utilizzando AWK:

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

Capire come funziona è lasciato come esercizio per il lettore.


grazie per questo breve corso imbarazzante!
darxmurf,

20
NR % 4 == 1sarebbe IMO più leggibile.
Stéphane Chazelas

12
Concordato su Stéphane; questo è probabilmente discutibile da parte mia, ma per domande potenzialmente da fare a casa cerco di offuscare un po 'le mie risposte ...
Stephen Kitt,

@StephenKitt offusca le tue risposte? Veramente? Questo non è il posto giusto per farlo.
dati

22

Utilizzando split (GNU coreutils):

split -nr/1/4 input > output
  • -ngenerare CHUNKSfile di output

e CHUNKScome

  • r/K/N usa la distribuzione round robin e invia Kth of N solo allo stdout senza dividere linee / record

1
Sbalordire. Risposte come queste sono le ragioni per cui adoro questa SE. Grazie!
user1717828

21

Con GNU sed:

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

Con standard sed:

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

Con 1e 4in $ne $ivariabili:

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

7

Aggiunta della soluzione obbligatoria perl:

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

4

Versione Python, solo per divertimento:

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

enumerate(f)dovrebbe essere in grado di fare il lavoro consumando meno memoria
iruvar

@iruvar È così pulito! Non l'avevo mai capito prima; userà in futuro. Sentiti libero di modificarlo in questa risposta; Non lo manterrò davvero con ottimizzazioni poiché le altre risposte di Bash (specialmente questa ) sono sicuramente la strada da percorrere.
user1717828,

Se hai intenzione di usare readlines(da cui deriva l'assorbimento dell'intero file in memoria), puoi usare f.readlines()[::4]per ottenere ogni quarta riga. Quindi puoi usare print(''.join(f.readlines()[::4])).
Nick Matteo

3

POSIX sed: questo metodo utilizza posix sed e quindi può essere eseguito ovunque, o almeno quelle sed che rispettano posix.

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

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

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

Un altro è una generazione di codice sed programmatica a fini di scalabilità:

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

Perl: riempiamo l'array A fino a quando non è di 4 dimensioni. Quindi stampiamo il suo primo elemento e cancelliamo anche l'array.

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

1

Chiama con scriptname filename skip(4 nel tuo caso) Funziona tirando le iterlinee dalla cima del file e poi solo emettendo l'ultimo. Quindi aumenta iterdi skipse si ripete finché il valore di iternon ha superato 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 è un builtin aggiunto in Bash 4 che legge l'input standard in un array, qui chiamato lines, con una riga per voce. L' -topzione rimuove le ultime righe finali.

Se si desidera stampare ogni quarta riga a partire dalla riga 4, è possibile farlo in un comando utilizzando mapfilel'opzione di callback -C, che esegue il codice fornito ogni tante righe, con l'intervallo indicato da -c. L'indice di array corrente e la riga successiva da assegnare vengono dati al codice come argomenti.

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

Questo utilizza l' printfintegrato; il codice di formato %.0ssopprime il primo argomento (l'indice), quindi viene stampata solo la riga.

Potresti usare lo stesso comando per stampare ogni quarta riga a partire dalla riga 1, 2 o 3, ma dovresti anteporre 3, 2 o 1 righe inputprima di alimentarlo mapfile, che penso sia più un problema di quanto valga la pena .

Questo funziona anche:

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

Qui, printfconsuma quattro voci dell'array linesalla volta, stampando solo la prima e saltando le altre tre %.0s. Non mi piace poiché devi manipolare manualmente la stringa di formato per intervalli o punti di partenza diversi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.