Imprimer des lignes impaires, imprimer des lignes paires

18

Je souhaite imprimer les lignes impaires et paires à partir des fichiers.

J'ai trouvé ce script shell qui utilise l'écho.

#!/bin/bash
# Write a shell script that, given a file name as the argument will write
# the even numbered line to a file with name evenfile and odd numbered lines
# in a text file called oddfile.
# -------------------------------------------------------------------------
# Copyright (c) 2001 nixCraft project <http://cyberciti.biz/fb/>
# This script is licensed under GNU GPL version 2.0 or above
# -------------------------------------------------------------------------
# This script is part of nixCraft shell script collection (NSSC)
# Visit http://bash.cyberciti.biz/ for more information.
# -------------------------------------------------------------------------

file=$1
counter=0

eout="evenfile.$$" # even file name
oout="oddfile.$$" # odd file name

if [ $# -eq 0 ]
then
    echo "$(basename $0) file"
    exit 1
fi

if [ ! -f $file ]
then
    echo "$file not a file"
    exit 2
fi

while read line
do
    # find out odd or even line number
    isEvenNo=$( expr $counter % 2 )

    if [ $isEvenNo -ne 0 ]
    then
        # even match
        echo $line >> $eout
    else
        # odd match
        echo $line >> $oout
    fi
    # increase counter by 1
    (( counter ++ ))
done < $file
echo "Even file - $eout"
echo "Odd file - $oout"

Mais n'y a-t-il pas moyen de le faire en une seule ligne?

Oui, utilisez awk, j'ai lu.

Lignes paires:

awk 'NR % 2' filename

lignes impaires:

awk 'NR % 2 == 1' filename

Mais ça ne marche pas pour moi. Les deux produisent la même sortie, selon diff. Par rapport au fichier d'origine, ils sont tous deux en fait deux fois moins longs et contiennent tous les deux des lignes impaires. Est-ce que je fais quelque chose de mal?

ixtmixilix
la source
6
Le premier devrait être NR % 2 == 0, sinon il est équivalent au second.
enzotib
Il semble y avoir plusieurs documents en ligne (y compris celui-ci) qui apparaissent en haut d'une recherche qui indiquent que NR% 2 vous donne les lignes numérotées paires, ce qui n'est pas correct, cela vous donne l'étrange parce que 1% 2 = 1 = vrai, 2% 2 = 0 = faux.
deltaray

Réponses:

12

Comme vous l'avez demandé «en une seule ligne»:

awk '{print>sprintf("%sfile.%d",NR%2?"odd":"even",PROCINFO["pid"])}' filename

Notez que la plupart du code est dû à votre choix de nom de fichier de sortie de fantaisie. Sinon, le code suivant serait suffisant pour mettre des lignes impaires dans "ligne-1" et des lignes paires dans "ligne-0":

awk '{print>"line-"NR%2}' filename
homme au travail
la source
26

Je préfère être compatible POSIX, dans la mesure du possible, j'ai donc pensé publier cette méthode alternative. Je les utilise souvent pour modifier le texte, avant les xargspipelines.

Imprimer des lignes paires,

sed -n 'n;p'

Imprimer des lignes impaires,

sed -n 'p;n'

Bien que j'utilise souvent awk, c'est exagéré pour ce type de tâche.

JM Becker
la source
14

C'est facile:

 sed -n 2~2p filename

imprimera les lignes paires à partir du nom de fichier

sed -n 1~2p filename

imprime des lignes impaires.

neuron34
la source
1
+1, pour ne pas utiliser AWK de manière externe. Pas POSIX sed, mais c'est toujours une méthode solide.
JM Becker
@TechZilla Je ne comprends pas "utiliser AWK de manière extrinsèque" - awk est aussi POSIX.
jw013
3
@ jw013: Rien ne cloche awk, personnellement je l'utilise très souvent. Je n'ai jamais dit que rien n'était «pas POSIX» awk, je faisais référence aux sedoptions de la réponse . Spécifiquement l' ~opérateur, c'est une extension GNU, qui est toujours acceptable pour beaucoup de gens. En ce qui concerne «l'utilisation d'AWK , I personally believe using extrêmement awk» pour cette tâche simple, c'est exagéré. Donc, le +1 était pour avoir terminé la tâche avec sed, un utilitaire plus léger que awk.
JM Becker
1
Quelqu'un peut-il expliquer comment fonctionne l'opérateur ~ ici?
Forever Learner
9

Pour les nombres pairs, le code doit être

awk 'NR%2==0' filename

& pour les nombres impairs

awk 'NR%2==1' filename
Neel
la source
1
celui-ci est parfait. Fonctionne même si vous devez obtenir des lignes par incréments de 10, par exemple, vous devez réduire un fichier commandé de 1 million à 100 Ko. C'est exactement ce que je voulais.
Dexter
Comment imprimer les colonnes paires dans AWK? Je ne peux pas faire fonctionner ça gawk 'FS=",";NF%2==0' file.csv.
hhh
2

Vous pouvez le faire avec une seule sedinvocation, pas besoin de lire le fichier deux fois:

sed '$!n
w even
d' infile > odd

ou, si vous préférez sur une seule ligne:

sed -e '$!n' -e 'w even' -e d infile > odd

Notez que ceux-ci ne donneront pas le résultat attendu si un fichier ne contient qu'une seule ligne (la ligne sera wécrite au evenlieu de oddcar la première nn'est pas exécutée). Pour éviter cela, ajoutez une condition:

sed -e '$!n' -e '1!{w even' -e 'd}' infile > odd

Comment ça fonctionne ? Eh bien, il utilise trois sedcommandes:
n- si ce n'est pas sur la dernière ligne, imprimez l'espace de motif vers stdout(qui est redirigé vers le fichier odd), remplacez-le par la ligne suivante (alors maintenant il traite une ligne paire) et continuez à exécuter les commandes restantes
w- ajoutez l'espace de motif à classer even
d- supprimez l'espace de motif actuel et redémarrez le cycle - l'effet secondaire est que sedcela n'imprimera jamais automatiquement l'espace de motif car il n'atteint jamais la fin du script

En d'autres termes, nest exécuté uniquement sur les lignes impaires et wet dn'est exécuté que sur les lignes paires. sedne parvient jamais à l'impression automatique à moins que, comme je l'ai dit, l'entrée se compose d'une seule ligne.

don_crissti
la source
pourriez-vous expliquer comment cela fonctionne?
Forever Learner
Merci beaucoup don_crissti pour votre aide. Je l'apprécie sincèrement, également voté.
Forever Learner
0

Essaye ça:

awk '{if(NR%2){print $0 > "odd.file"}else{print $0 > "even.file"}}' filename
renma
la source
Voulez-vous vraiment sortir les numéros d'enregistrement?
manatwork
désolé, je l'ai modifié pour afficher toutes les lignes.
renma
0

J'irais avec perlparce que j'aime perl:

perl -pe 'BEGIN{open($e,">even_lines");open($o,">odd_lines")} $. % 2 ?select $o:select $e;'

Utilise le fait d' -pimprimer implicitement, pour reproduire le sedfonctionnement - et nous utilisonsselect pour choisir le descripteur de fichier dans lequel il écrit.

Sobrique
la source