Como faço para copiar e colar linhas entre uma palavra-chave de início e de fim?

1

Eu tenho dois arquivos de texto e quero copiar várias linhas de uma para outra. O arquivo um tem uma lista de pacotes e eu quero copiá-lo para a lista dois. Esta lista de pacotes não está no início do arquivo um, mas tem uma tag% packages no início da lista e uma tag% end no final. Eu estou querendo saber como eu posso copiar todas as linhas entre% packages e% end do arquivo 1 para o arquivo 2?

    
por Centimane 16.07.2014 / 22:11

3 respostas

4

Para copiar todas as linhas entre% packages e% end de file1 para file2:

awk '$1=="%end" {f=0;next} f{print;next} $1=="%packages" {f=1}' file1 >>file2

Esta solução foi projetada para remover as linhas% packages e% end. (Se você quiser que essas linhas sejam transferidas também, existe uma solução ainda mais simples abaixo.)

Como awk implicitamente percorre todas as linhas de um arquivo, os comandos acima são aplicados a cada linha em file1 . Ele usa um sinalizador, chamado f , para determinar se estamos na seção de pacotes de file1 . Cada linha dentro da seção de pacotes é impressa no stdout que é redirecionado para file2 .

Vamos considerar os comandos awk , um por um:

  • $1=="%end" {f=0;next}

    Este comando verifica se a linha começa com %end . Em caso afirmativo, o sinalizador f é definido como zero e passamos para a linha next .

  • f{print;next}

    Este comando verifica se o sinalizador f é diferente de zero. Se for diferente de zero, a linha será impressa e passaremos para a próxima linha.

  • $1=="%packages" {f=1}

    Este comando verifica se a linha começa com %packages . Se assim for, define o sinalizador f para um, para que as linhas depois disso sejam impressas.

Incluindo as linhas dos marcadores:

O acima exclui as linhas de marcação% packages e% end. Se você quiser aqueles incluídos, use:

awk '/^%packages/,/^%end/ {print}' file1 >>file2
    
por 16.07.2014 / 22:16
2

Além do awk, outra solução a considerar é sed:

sed -n '/%packages/,/%end/ w file2' file1

quebra por ordem de aparição:
sed em si, obviamente, seguido por uma abertura ' . Isso informa ao sed tudo desse ponto até fechar ' é um argumento / comando para sed. Tudo depois disso é entrada (ou saída se estiver usando redirecionamento > arquivo)

-n Suprime a impressão. Sem ele, todo o conteúdo do arquivo1 será impresso, com o texto correspondente impresso duas vezes

/pattern1/,/pattern2/ Define os limites do intervalo para corresponder

w file Escreva no arquivo. Deve ser o último argumento, seguido pelo nome do arquivo (ou / caminho / para / arquivo, se não estiver no diretório atual)

Finalmente, depois de fechar o único ' , temos o arquivo de entrada.

Duas notas finais:

1.Alguma vez prefere usar o redirecionamento para o arquivo de entrada, então o comando final se parece com:

sed -n '/%packages/,/%end/ w file2' <file1

A vantagem é mais clareza - é óbvio onde você recebe sua contribuição. Da mesma forma, em vez de usar w file , você pode redirecionar o arquivo de saída >:

sed -n '/%packages/,/%end/ p' <file1 >file2

Nesse caso, adicionamos p para imprimir a correspondência (substituir -n para seleção)

No entanto, o sed pode operar em vários arquivos de entrada:

sed -n '/%packages/,/%end/ w file-final' file1 file2 file3

e o uso de redirecionamento tendem a cegar os usuários para esse recurso.

2.As correspondências acima, incluindo as linhas inicial e final, como sed opera no nível da linha, não no nível da palavra. Uma solução pode ser simplesmente canalizar para mais sed:

sed -n '/%packages/,/%end/ w file2' file1 | sed -e '1d' -e '$d'

que apresenta os novos recursos a seguir:
-e permite executar vários comandos na mesma entrada
1 mostra que a numeração de linhas corresponde ao trabalho
d é para exclusão de padrão de correspondência - número de linha 1 no primeiro comando
$ é o final do fluxo de entrada. Como o sed opera na linha, e não no nível da palavra, a linha inteira no final é excluída

Mas, na verdade, podemos fazer isso em uma única chamada de sed, usando chaves para agrupar (aqui em um script para maior clareza):

#!/bin/bash
sed -n '
  /%packages/,/%end/ {
    /%packages/n
    /%end/ !{
      w file2
    }
  }
' file1

A única coisa nova aqui (além do agrupamento) é usar ! para negar a correspondência.
/pattern/n Suprime a linha de impressão com padrão (o mesmo que -n no início). /pattern/ ! Seleciona tudo que NÃO corresponde ao padrão (correspondência reversa). A razão, por coincidência, é bem simples. se fizéssemos outro /%end/n para suprimir o padrão% end, também o impediríamos de limitar nosso intervalo, e o arquivo seria impresso no final.

    
por 17.07.2014 / 02:26
1

Mais fácil de entender:

grep -A 1000 '%packages' xx | grep -B 1000 '%end'

A primeira parte procura por %packages e imprime 1000 linhas (incluindo a linha correspondente) A após.

Segunda parte após o pipe: Procura %end e imprime todas as 1000 linhas (incluindo a linha correspondente) B antes.

Se o arquivo tiver mais de 1000 linhas, altere o número 1000 para um número maior.

E se você quiser apenas linhas de correspondência somente , que não tenham nada além do padrão de pesquisa, inclua o início e término da expressão regular, i. e.

grep -A 1000 '^%packages$' xx | grep -B 1000 '^%end$'

Se você não quiser incluir as linhas correspondentes, adicione outro canal:

grep -A 1000 '^%packages$' xx | grep -B 1000 '^%end$' | grep -v -e '^%packages$' -e '^%end$'

em que -e pode ser usado para especificar vários padrões de pesquisa e -v é usado para inverter o sentido de correspondência, para selecionar linhas não correspondentes.

    
por 17.07.2014 / 02:49