it-swarm-pt.tech

Como posso extrair um intervalo predeterminado de linhas de um arquivo de texto no Unix?

Eu tenho um despejo de SQL de linha ~ 23000 contendo vários bancos de dados no valor de dados. Eu preciso extrair uma certa seção deste arquivo (ou seja, os dados para um único banco de dados) e colocá-lo em um novo arquivo. Conheço os números de linha inicial e final dos dados que desejo.

Alguém sabe um comando Unix (ou uma série de comandos) para extrair todas as linhas de um arquivo entre as linhas 16224 e 16482 e depois redirecioná-las para um novo arquivo?

447
Adam J. Forster
sed -n '16224,16482p;16483q' filename > newfile

Do sed manual :

p - Imprime o espaço do padrão (para a saída padrão). Esse comando geralmente é usado apenas em conjunto com a opção de linha de comando -n.

n - Se a impressão automática não estiver desativada, imprima o espaço de padrão e, de qualquer forma, substitua o espaço de padrão pela próxima linha de entrada. Se Não houver mais entrada, o sed sairá sem processar mais comandos .

q - Sair sed sem processar mais nenhum comando ou entrada. Observe que o espaço de padrão atual é impresso se a impressão automática não estiver desativada com a opção -n.

e

Endereços em um script sed podem estar em qualquer um dos seguintes formatos:

number Especificar um número de linha corresponderá somente àquela linha na entrada.

Um intervalo de endereços pode ser especificado especificando dois endereços Separados por uma vírgula (,). Um intervalo de endereços corresponde a linhas que começam em Onde o primeiro endereço corresponde e continua até que o segundo endereço Corresponda (inclusive).

681
boxxar
sed -n '16224,16482 p' orig-data-file > new-file

Onde 16224,16482 são o número da linha inicial e o número da linha final, inclusive. Este é 1 indexado. -n suprime o eco da entrada como saída, o que você claramente não quer; os números indicam o intervalo de linhas para fazer o seguinte comando operar; o comando p imprime as linhas relevantes.

195
JXG

Bastante simples usando cabeça/cauda:

head -16482 in.sql | tail -258 > out.sql

usando sed:

sed -n '16482,16482p' in.sql > out.sql

usando o awk:

awk 'NR>=10&&NR<=20' in.sql > out.sql
78
manveru

Você poderia usar 'vi' e, em seguida, o seguinte comando:

:16224,16482w!/tmp/some-file

Alternativamente: 

cat file | head -n 16482 | tail -n 258

EDIT: - Só para adicionar uma explicação, você usa head -n 16482 para exibir as primeiras 16482 linhas e então usar tail -n 258 para obter as últimas 258 linhas da primeira saída. 

25
Mark Janssen

Existe outra abordagem com awk:

awk 'NR==16224, NR==16482' file

Se o arquivo for grande, pode ser bom exit depois de ler a última linha desejada. Desta forma, não lerá desnecessariamente o arquivo até o final:

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file
20
fedorqui
Perl -ne 'print if 16224..16482' file.txt > new_file.txt
14
mmaibaum
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2
8
Cetra

sed -n '16224,16482p' < dump.sql

5
cubex
cat dump.txt | head -16224 | tail -258

deve fazer o truque. A desvantagem dessa abordagem é que você precisa fazer a aritmética para determinar o argumento da cauda e explicar se você quer que o 'entre' inclua a linha final ou não.

5
JP Lodine

Rapido e sujo:

head -16428 < file.in | tail -259 > file.out

Provavelmente não é a melhor maneira de fazer isso, mas deve funcionar.

BTW: 259 = 16482-16224 + 1.

3
jan.vdbergh

Eu estava prestes a postar o truque de cabeça/cauda, ​​mas na verdade eu provavelmente apenas acionaria o emacs. ;-)

  1. esc-x linha de goto ret 16224
  2. marca (ctrl-space)
  3. esc-x linha de goto ret 16482
  4. esc-w

abra o novo arquivo de saída, ctl-y save

Me deixe ver o que está acontecendo.

3
sammyo

Mesmo nós podemos fazer isso para verificar na linha de comando:

cat filename|sed 'n1,n2!d' > abc.txt

Por exemplo:

cat foo.pl|sed '100,200!d' > abc.txt
2
Chinmoy Padhi

Usando Ruby:

Ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf
2
Carl Blakeley

Eu usaria:

awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt

FNR contém o número de registro (linha) da linha que está sendo lida do arquivo.

2
Paddy3118

Eu escrevi um programa do Haskell chamado splitter que faz exatamente isso: ter um ler meu post no blog de lançamento .

Você pode usar o programa da seguinte maneira:

$ cat somefile | splitter 16224-16482

E isso é tudo o que existe para isso. Você precisará do Haskell para instalá-lo. Somente:

$ cabal install splitter

E você está feito. Espero que você ache este programa útil.

2
Robert Massaioli

Isso pode funcionar para você (GNU sed):

sed -ne '16224,16482w newfile' -e '16482q' file

ou aproveitando o bash:

sed -n $'16224,16482w newfile\n16482q' file
1
potong

Eu escrevi um pequeno script bash que você pode executar a partir de sua linha de comando, desde que você atualize seu PATH para incluir seu diretório (ou você pode colocá-lo em um diretório que já esteja contido no PATH).

Uso: $ pinch filename end-line end-line

#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon

FILENAME=$1
START=$2
END=$3

ERROR="[PINCH ERROR]"

# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
    echo "$ERROR Need three arguments: Filename Start-line End-line"
    exit 1
fi

# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
    echo -e "$ERROR File does not exist. \n\t$FILENAME"
    exit 1
fi

# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
    echo -e "$ERROR Start line is greater than End line."
    exit 1
fi

# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
    echo -e "$ERROR Start line is less than 0."
    exit 1
fi

# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
    echo -e "$ERROR End line is less than 0."
    exit 1
fi

NUMOFLINES=$(wc -l < "$FILENAME")

# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
    echo -e "$ERROR End line is greater than number of lines in file."
    exit 1
fi

# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))

# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
    < "$FILENAME" head -n $END | tail -n +$START
else
    < "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi

# Success
exit 0
1
Nerdfighter

Eu queria fazer a mesma coisa de um script usando uma variável e consegui-lo colocando aspas ao redor da variável $ para separar o nome da variável do p:

sed -n "$first","$count"p imagelist.txt >"$imageblock"

Eu queria dividir uma lista em pastas separadas, encontrei a pergunta inicial e respondi uma etapa útil. (comando split não é uma opção no sistema operacional antigo que eu tenho que portar código para).

1
KevinY

Como estamos falando de extrair linhas de texto de um arquivo de texto, darei um caso especial em que você deseja extrair todas as linhas que correspondam a um determinado padrão. 

myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile

Vai imprimir a linha [Dados] e o restante. Se você quiser que o texto da linha 1 ao padrão, digite: sed -n '1,/Data/p' myfile. Além disso, se você conhecer dois padrões (melhor ser exclusivo em seu texto), tanto a linha inicial como a final do intervalo poderão ser especificadas com correspondências.

sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
0
Kemin Zhou

De pé sobre os ombros do boxxar, eu gosto disso:

sed -n '<first line>,$p;<last line>q' input

por exemplo.

sed -n '16224,$p;16482q' input

O $ significa "última linha", então o primeiro comando faz sed imprimir todas as linhas iniciando com a linha 16224 e o segundo comando faz sed quit after linha de impressão 16428. (Adicionar 1 para o intervalo q- na solução boxxar não parece ser necessário.)

Eu gosto dessa variante porque não preciso especificar o número da linha final duas vezes. E eu medi que usar $ não tem efeitos prejudiciais no desempenho.

0
Tilman Vogel

O -n nas respostas aceitas funciona. Aqui está outra maneira no caso de você estar inclinado.

cat $filename | sed "${linenum}p;d";

Isso faz o seguinte:

  1. canalizar o conteúdo de um arquivo (ou inserir o texto como quiser).
  2. sed seleciona a linha dada, imprime
  3. d é necessário para excluir linhas, caso contrário, o sed assumirá que todas as linhas serão eventualmente impressas. isto é, sem o d, você terá todas as linhas impressas pela linha selecionada impressas duas vezes porque você tem a parte $ {linenum} p pedindo para que seja impressa. Tenho certeza que o -n está basicamente fazendo a mesma coisa que o d aqui.
0
ThinkBonobo