Excluir número (s) de linha específico (s) de um arquivo de texto usando o sed?

235

Quero excluir um ou mais números de linha específicos de um arquivo. Como eu faria isso usando o sed?

Justin Ethier
fonte
1
Você pode dar um exemplo mais específico do que deseja? Como você decide quais linhas remover?
precisa
Talvez veja também stackoverflow.com/questions/13272717/… e apenas aplique-o ao contrário (imprima se a chave não estiver no array associativo).
Tripleee

Respostas:

374

Se você deseja excluir as linhas 5 a 10 e 12:

sed -e '5,10d;12d' file

Isso imprimirá os resultados na tela. Se você deseja salvar os resultados no mesmo arquivo:

sed -i.bak -e '5,10d;12d' file

Isso fará o backup do arquivo file.bake excluirá as linhas fornecidas.

Nota: Os números das linhas começam em 1. A primeira linha do arquivo é 1, não 0.

Brian Campbell
fonte
32
Nem todos os unixes possuem o gnu sed com "-i". Não cometa o erro de voltar para "sed cmd file> file", o que destruirá seu arquivo.
pra
4
what Se eu quisesse excluir a quinta linha até a última linha?
Jürgen Paul
14
@WearetheWorldsed -e '5,$d' file
Brian Campbell
1
@BrianCampbell O que devo fazer para excluir apenas uma linha específica?
Kanagavelu Sugumar
14
@KanagaveluSugumar sed -e '5d' file. A sintaxe é <address><command>; onde <address>pode ser uma única linha 5ou um intervalo de linhas 5,10, e o comando dexclui a linha ou linhas especificadas. Os endereços também podem ser expressões regulares ou o cifrão $indicando a última linha do arquivo.
Brian Campbell
50

Você pode excluir uma única linha específica com seu número de linha pressionando

sed -i '33d' file

Isso excluirá a linha no número da linha 33 e salvará o arquivo atualizado.

amit
fonte
1
No meu caso, "sed" removeu uma linha errada. Então eu usar essa abordagem: sed -i '0,/<TARGET>/{/<NEW_VALUE>/d;}' '<SOME_FILE_NAME>'. Obrigado!
Eduardo Lucio
Mesmo aqui, escrevi um loop e estranhamente alguns arquivos perderam a linha correta, mas alguns arquivos também perderam uma outra linha, não fazem ideia do que deu errado. (GNU / Linux bash4.2) O comando awk abaixo funcionou bem em loop
FatihSarigol 5/18
Tenha muito cuidado ao usar tipo -r se você estiver excluindo a partir de uma lista de linhas, caso contrário, o seu primeiro sed irá alterar os números de linha de tudo o mais ...!
Konchog
Para comentários sobre linhas erradas sendo excluído dentro de um loop: certifique-se de começar com o maior número de linha, caso contrário, cada linha excluída irá compensar a numeração de linha ...
Skippy le Grand Gourou
25

e awk também

awk 'NR!~/^(5|10|25)$/' file
ghostdog74
fonte
2
NB: Essa linha awk trabalhou de forma mais confiável para mim do que a variante sed (entre OS X e Ubuntu Linux)
Jay Taylor
3
Observe que isso não exclui nada no arquivo. Apenas imprime o arquivo sem essas linhas em stdout. Portanto, você também precisa redirecionar a saída para um arquivo temporário e, em seguida, mova o arquivo temporário para substituir o original.
mivk
17
$ cat foo
1
2
3
4
5
$ sed -e '2d;4d' foo
1
3
5
$ 
Matthew Slattery
fonte
6

Muitas vezes, isso é um sintoma de um antipadrão. A ferramenta que produziu os números das linhas pode muito bem ser substituída por uma que exclua as linhas imediatamente. Por exemplo;

grep -nh error logfile | cut -d: -f1 | deletelines logfile

(onde deletelinesestá o utilitário que você imagina que precisa) é o mesmo que

grep -v error logfile

Dito isto, se você estiver em uma situação em que realmente precisa executar esta tarefa, poderá gerar um sedscript simples a partir do arquivo de números de linha. Bem humorado (mas talvez um pouco confuso), você pode fazer isso sed.

sed 's%$%d%' linenumbers

Isso aceita um arquivo de números de linha, um por linha, e produz, na saída padrão, os mesmos números de linha danexados após cada um. Este é um sedscript válido , que podemos salvar em um arquivo ou (em algumas plataformas) canalizar para outra sedinstância:

sed 's%$%d%' linenumbers | sed -f - logfile

Em algumas plataformas, sed -fnão entende o argumento de opção -como entrada padrão, portanto, você deve redirecionar o script para um arquivo temporário e limpá-lo quando terminar, ou talvez substituir o traço solitário por /dev/stdinou /proc/$pid/fd/1se o seu SO (ou shell ) tem isso.

Como sempre, você pode adicionar -iantes da -fopção a sededição do arquivo de destino, em vez de produzir o resultado na saída padrão. Nas plataformas * BSDish (incluindo OSX), você também precisa fornecer um argumento explícito -i; um idioma comum é fornecer um argumento vazio; -i ''.

triplo
fonte
Não concordo totalmente com "sintoma de um antipadrão". Os tipos de arquivo baseados em marcação (por exemplo, XML ou JSON) requerem linhas específicas no final para serem arquivos válidos. Nesse caso, geralmente é a abordagem mais razoável para remover essas linhas, colocar no arquivo o que você deseja adicionar e depois adicioná-las novamente, porque colocar as linhas imediatamente pode ser muito mais trabalhoso e vai contra o desejo potencial de evitar ferramentas extras como o sed, tanto quanto possível.
Egor Hans
Não entendo bem que tipo de cenário você está imaginando. Não são cenários onde esta é uma abordagem legítima, mas a grande maioria dos casos que tenho visto são novatos que fazem mais ou menos exatamente o que o meu primeiro exemplo demonstra. (Talvez eles vêm de alguma linguagem muito baixo nível e são usados para dividir o seu caminho problema passado, o nível molecular, porque você tem que, em asm ou C.)
tripleee
Remover coisas pelo número da linha de XML ou JSON parece extremamente quebradiço, se não totalmente perigoso.
tripleee
O que quero dizer com isso basicamente é que, como criador de um arquivo, você sabe o que deve ser no final do documento (ou seja, o conjunto de chaves de fechamento / colchetes nas últimas linhas para JSON, ou o código exato tags de fechamento para XML). Ciente disso, a abordagem mais simples para estender esse documento é 1) remova as últimas linhas, 2) adicione o novo conteúdo, 3) adicione novamente as últimas linhas. Dessa forma, o documento pode ser válido antes e depois de ter sido estendido, sem a necessidade de encontrar uma maneira de adicionar linhas no meio do documento.
Egor Hans
1
Até agora, esta é a única resposta com uma solução apropriada para um grande número de linhas (isto é, fornecidas por um arquivo). E o prefácio também faz sentido. Merece mais votos. BTW, se você quiser imprimir linhas em vez de excluí-las, use em pvez de d, junto com a opção -n(não funcionará sem -ne !dtambém não).
Skippy le Grand Gourou
2

Eu gostaria de propor uma generalização com o awk.

Quando o arquivo é criado por blocos de tamanho fixo e as linhas a serem excluídas são repetidas para cada bloco, o awk pode funcionar bem dessa maneira

awk '{nl=((NR-1)%2000)+1; if ( (nl<714) || ((nl>1025)&&(nl<1029)) ) print  $0}'
 OriginFile.dat > MyOutputCuttedFile.dat

Neste exemplo, o tamanho do bloco é 2000 e quero imprimir as linhas [1..713] e [1026..1029].

  • NR é a variável usada pelo awk para armazenar o número da linha atual.
  • % fornece o restante (ou módulo) da divisão de dois números inteiros;
  • nl=((NR-1)%BLOCKSIZE)+1Aqui, escrevemos na variável nl o número da linha dentro do bloco atual. (ver abaixo)
  • ||e &&são o operador lógico OR e AND .
  • print $0 escreve a linha completa

Why ((NR-1)%BLOCKSIZE)+1:
(NR-1) We need a shift of one because 1%3=1, 2%3=2, but 3%3=0.
  +1   We add again 1 because we want to restore the desired order.

+-----+------+----------+------------+
| NR  | NR%3 | (NR-1)%3 | (NR-1)%3+1 |
+-----+------+----------+------------+
|  1  |  1   |    0     |     1      |
|  2  |  2   |    1     |     2      |
|  3  |  0   |    2     |     3      |
|  4  |  1   |    0     |     1      |
+-----+------+----------+------------+

Hastur
fonte
2
Admiro a maneira como você vive de acordo com seu nome indutor de loucura.
Jukka Dahlbom