Imprime linhas ímpares, imprime linhas pares

13

Eu quero imprimir as linhas ímpares e pares de arquivos.

Eu encontrei este script de shell que faz uso de eco.

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

Mas não há como fazer isso em uma linha?

Sim, use o awk, eu li.

Linhas com numeração par:

awk 'NR % 2' filename

linhas de numeração ímpar:

awk 'NR % 2 == 1' filename

Mas isso não funciona para mim. Ambos produzem a mesma saída, de acordo com o diff. Quando comparado com o arquivo original, eles são na verdade metade do tempo, e ambos contêm as linhas ímpares. Estou fazendo algo errado?

    
por ixtmixilix 13.12.2011 / 12:36

7 respostas

11

Como você perguntou "em uma linha":

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

Observe que a maior parte do código se deve à sua escolha de nome de arquivo de saída. Caso contrário, o código a seguir seria suficiente para colocar linhas ímpares em "linha 1" e até linhas em "linha 0":

awk '{print>"line-"NR%2}' filename
    
por 13.12.2011 / 12:55
18

Eu prefiro ser compatível com POSIX, sempre que possível, então pensei em postar esse método alternativo. Eu geralmente os uso para manipular texto, antes de xargs pipelines.

Imprimir linhas numeradas pares,

sed -n 'n;p'

Imprimir linhas ímpares numeradas,

sed -n 'p;n'

Embora eu use frequentemente awk , é um exagero para esse tipo de tarefa.

    
por 01.11.2012 / 15:42
13

Isso é fácil:

 sed -n 2~2p filename

imprime linhas pares do nome do arquivo

sed -n 1~2p filename

imprime linhas ímpares.

    
por 13.12.2011 / 16:05
4

Para números pares, o código deve ser

awk 'NR%2==0' filename

& para números ímpares

awk 'NR%2==1' filename
    
por 09.12.2015 / 19:24
2

Você pode fazer isso com uma única invocação sed , não é necessário ler o arquivo duas vezes:

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

ou, se você preferir em uma linha:

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

Observe que isso não dará o resultado esperado se um arquivo contiver apenas uma linha (a linha será w ritten para even em vez de odd , pois o primeiro n não será executado). Para evitar isso, adicione uma condição:

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

Como funciona? Bem, ele usa três sed comandos:
n - se não na última linha imprime o espaço padrão para stdout (que é redirecionado para o arquivo odd ), substitua com a próxima linha (então agora está processando uma linha par) e continue executando os comandos restantes em w - acrescente o espaço padrão para o arquivo even e d - apague o espaço padrão atual e reinicie o ciclo - o efeito colateral disso é que sed nunca irá imprimir automaticamente o espaço de padrão, uma vez que nunca chega ao final do script

Em outras palavras, n é executado apenas em linhas ímpares e w e d são executados apenas em linhas pares. sed nunca consegue imprimir automaticamente, a menos que, como eu disse, a entrada consiste em uma única linha.

    
por 23.09.2015 / 22:06
0

Tente isto:

awk '{if(NR%2){print $0 > "odd.file"}else{print $0 > "even.file"}}' filename
    
por 13.12.2011 / 13:14
0

Eu usaria perl porque gosto de perl :

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

Usa o fato de que -p imprime implicitamente, para replicar como sed funciona - e usamos select para escolher qual identificador de arquivo para o qual ele grava.

    
por 09.12.2015 / 20:51