Imprima líneas impares, imprima líneas pares

18

Quiero imprimir las líneas impares y pares de los archivos.

Encontré este script de shell que hace uso de echo.

#!/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"

¿Pero no hay una manera de hacerlo en una línea?

Sí, usa awk, leí.

Líneas pares:

awk 'NR % 2' filename

líneas impares:

awk 'NR % 2 == 1' filename

Pero no me funciona. Ambos producen la misma salida, según diff. En comparación con el archivo original, ambos tienen la mitad de longitud y ambos contienen las líneas impares. ¿Estoy haciendo algo mal?

ixtmixilix
fuente
66
El primero debe ser NR % 2 == 0, de lo contrario es equivalente al segundo.
enzotib
Parece que hay varios documentos en línea (incluido este) que aparecen en la parte superior de una búsqueda que indican que NR% 2 le da las líneas pares, lo cual no es correcto, le da lo impar porque 1% 2 = 1 = verdadero, 2% 2 = 0 = falso.
deltaray

Respuestas:

12

Como preguntaste "en una línea":

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

Tenga en cuenta que la mayor parte del código se debe a su elección de nombre de archivo de salida elegante. De lo contrario, el siguiente código sería suficiente para poner líneas impares en "línea-1" y líneas pares en "línea-0":

awk '{print>"line-"NR%2}' filename
hombre trabajando
fuente
26

Prefiero ser compatible con POSIX, siempre que sea posible, así que pensé en publicar este método alternativo. A menudo los uso para alterar el texto, antes de las xargstuberías.

Imprimir incluso líneas numeradas,

sed -n 'n;p'

Imprimir líneas numeradas impares,

sed -n 'p;n'

Aunque a menudo lo uso awk, es excesivo para este tipo de tarea.

JM Becker
fuente
14

Eso es fácil:

 sed -n 2~2p filename

imprimirá líneas pares del nombre del archivo

sed -n 1~2p filename

imprimirá líneas impares.

neurona34
fuente
1
+1, por no usar AWK de forma extraña. No POSIX sed, pero sigue siendo un método sólido.
JM Becker
@TechZilla No entiendo "usar AWK de forma extraña" - awk es POSIX también.
jw013
3
@ jw013: No hay nada malo awk, personalmente lo uso con mucha frecuencia. Nunca dije que se tratara de 'No POSIX' awk, me refería a las sedopciones de respuesta . Específicamente el ~operador, es una extensión GNU, que todavía es aceptable para muchas personas. Respecto a 'usar AWK , I personally believe using extrañamente awk' para esta tarea simple es excesivo. Entonces, el +1 fue para completar la tarea con seduna utilidad más ligera que awk.
JM Becker
1
¿Alguien puede explicar cómo funciona el operador ~ aquí?
Forever Learner
9

Para números pares, el código debe ser

awk 'NR%2==0' filename

y para números impares

awk 'NR%2==1' filename
Neel
fuente
1
Este es perfecto. Incluso funciona si necesita obtener filas en incrementos de 10, digamos que necesita reducir un archivo ordenado de tamaño de 1 millón a 100k. Esto es exactamente lo que quería.
Dexter
¿Cómo puede imprimir las columnas pares en AWK? No puedo hacer que esto funcione gawk 'FS=",";NF%2==0' file.csv.
hhh
2

Puede hacerlo con una sola sedinvocación, no es necesario leer el archivo dos veces:

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

o, si lo prefiere en una línea:

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

Tenga en cuenta que estos no darán el resultado esperado si un archivo contiene solo una línea (la línea se repetirá wen evenlugar de oddque la primera nno se ejecute). Para evitar eso, agregue una condición:

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

Cómo funciona ? Bueno, utiliza tres sedcomandos:
nsi no está en la última línea, imprima el espacio del patrón stdout(que se redirige al archivo odd), reemplácelo con la siguiente línea (por lo que ahora está procesando una línea uniforme) y continúe ejecutando los comandos restantes
w: agregue el espacio de patrón para archivar even
d: elimine el espacio de patrón actual y reinicie el ciclo; el efecto secundario de esto es que sednunca imprimirá automáticamente el espacio de patrón ya que nunca llega al final del script

En otras palabras, nse ejecuta sólo en líneas pares e wy dson ejecutados sólo en líneas pares. sednunca llega a la impresión automática a menos que, como dije, la entrada consista en una sola línea.

don_crissti
fuente
¿podría por favor elaborar cómo funciona?
Forever Learner
Muchas gracias don_crissti por tu ayuda. Sinceramente lo aprecio, también votado.
Forever Learner
0

Prueba esto:

awk '{if(NR%2){print $0 > "odd.file"}else{print $0 > "even.file"}}' filename
renma
fuente
¿Está seguro de generar los números de registro?
manatwork
perdón por eso, lo modifiqué para generar las líneas completas.
renma
0

Yo iría perlporque me gusta perl:

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

Utiliza el hecho de que -pimprime implícitamente, para replicar cómo sedfunciona, y usamos selectpara elegir qué identificador de archivo en el que escribe.

Sobrique
fuente