AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 570101
Accepted
Tagwint
Tagwint
Asked: 2020-02-28 08:52:33 +0800 CST2020-02-28 08:52:33 +0800 CST 2020-02-28 08:52:33 +0800 CST

Modificar linhas não adjacentes especificadas por números de linha

  • 772

Conheço os números de linha com antecedência e os mantenho em outro arquivo:

cat linenos
2
15
42
44
... etc

como você vê, as linhas não são adjacentes, então não posso usar um intervalo para sed. O objetivo é modificar as linhas do arquivo de destino, digamos, prefixando-as com um marcador como MARKER

A maneira direta é chamar sedvárias vezes para modificar cada linha:

for l in $(cat linenos)
do 
  sed -i "${l}s/^/MARKER/" target_file
done

que aparentemente chamará sed várias vezes.

CUIDADO : *Esta abordagem não é apenas ineficiente, mas também pode fazer com que as coisas dêem errado se a modificação for diferente de inserir um marcador como este. Qualquer comando sed de exclusão ou inserção de linha, como dar, tornará o número de linha inicial em linenos inválido para as próximas execuções do sed no loop.

O que você sugeriria para melhorar/otimizar isso?

Exemplo de arquivo linenos

cat linenos
2
5

Exemplo de arquivo_destino

cat target_file
line one
line two
line three
line four
line five
line six

Resultado esperado de target_file modificado

cat target_file
line one
MARKERline two
line three
line four
MARKERline five
line six

A abordagem possível que eu criei é criar dinamicamente o cenário sed

SEDCMD=$(for l in $(cat linenos); do echo -n "${l}s/^/MARK/;" ; done)

sed -i -e "$SEDCMD" targetfile

A abordagem abaixo do @steeldriver compartilha a ideia, mas é mais elegante e concisa

bash sed
  • 5 5 respostas
  • 433 Views

5 respostas

  • Voted
  1. steeldriver
    2020-02-28T09:31:41+08:002020-02-28T09:31:41+08:00

    Você pode usar o próprio sed (ou outro utilitário de processamento de texto de sua escolha) para transformar os números de linha em expressões sed e depois passá-los para o sed usando a -fopção

    Ex.

    sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file
    

    Isso pelo menos só chama sed duas vezes .

    • 10
  2. Stéphane Chazelas
    2020-02-28T11:20:00+08:002020-02-28T11:20:00+08:00

    Com perl(de onde o GNU sedveio -i):

    perl -pi -e '
      BEGIN{$l{0+$_}=1 while <STDIN>}
      $_ = "MARKER$_" if $l{$.}' target_file < linenos
    

    Alimentamos a lista de números de linha no perlstdin de . Isso é lido no BEGINbloco.

    Para cada linha de entrada, convertemos essa linha em um número com 0+$_. Isso faz com que o caractere de nova linha desapareça e também canoniza os números (todos 1e0, 1, 01 se tornam 1).

    A %ltabela de hash é preenchida com valor 1para cada número de linha como a chave.

    O target_fileé processado no -ploop principal onde MARKERSé anexado às linhas em que o número da linha atual ( $.) é encontrado %lcom um valor diferente de zero.

    • 6
  3. Ed Morton
    2020-02-28T13:44:46+08:002020-02-28T13:44:46+08:00
    $ awk 'NR==FNR{a[$1]="MARKER"; next} {print a[FNR] $0}' linenos target_file
    line one
    MARKERline two
    line three
    line four
    MARKERline five
    line six
    

    ou para economizar um pouco de memória:

    $ awk 'NR==FNR{a[$1]; next} {print (FNR in a ? "MARKER" : "") $0}' linenos target_file
    line one
    MARKERline two
    line three
    line four
    MARKERline five
    line six
    

    Se você quiser edição "inplace" (o mesmo que perl e GNU sed com -i) use GNU awk e altere awk '...'e awk -i inplace '...'adicione um print;antes da nextinstrução para que seu linenosarquivo não seja esvaziado. IMHO é mais simples fazer isso com qualquer awk (ou qualquer outra ferramenta UNIX):

    awk 'script' linenos target_file > tmp && mv tmp target_file
    
    • 4
  4. Best Answer
    user232326
    2020-03-01T11:23:56+08:002020-03-01T11:23:56+08:00

    Se fileNcontém o número de linhas a serem modificadas, e target_fileé o arquivo de texto que contém as linhas a serem modificadas. A solução mínima exigirá a leitura de cada arquivo uma vez.

    Classificado

    Se o arquivo que contém os números das linhas contiver um número (maior que 1) por linha, estiver ordenado e não houver repetições, podemos usar:

    awk 'BEGIN{ getline lineN <"fileN"} {
         if(NR==lineN){$0="MARKER " $0;getline lineN <"fileN"}
         }1' target_file
    

    O que manterá apenas uma linha na memória (de cada arquivo) e percorrerá os dois arquivos do início ao fim. No entanto, uma vez que o awk tenha processado uma linha, a linha 15, por exemplo, ele não voltará à linha 12, por exemplo. Portanto, o arquivo lineNdeve ser classificado (não repetido e maior que 1) para que isso funcione.

    não triados

    Claro, a solução ingênua é que o arquivo de números de linha pode ser classificado sort -nu fileN.

    Mas, se a lista de números de linha puder ser desordenada (e repetida), podemos usar sed , ed(o precursor de sed), ou awk (mais tarde):

    Converta cada linha em lineNum comando de edição sed como s/^/MARKER /. Ou shell printf ou sed poderia fazer isso:

    printf '%ss/^/MARKER /\n' $(<fileN) | sed -f - target_file
    sed 's#$#s/^/MARKER /#' fileN       | sed -f - target_file
    
    { printf '%ss/^/MARKER /\n' $(<fileN); printf '%s\n' ,p Q; } | ed -Gs target_file
    { sed 's#$#s/^/MARKER /#' fileN ; echo "w"      ; } | ed target_file
    

    Observe que no último caso a edição é feita diretamente e no arquivo original. O último comando wgrava as modificações no arquivo. Se for necessário imprimir o resultado, use a terceira opção, que imprimirá todas as linhas.

    awk

    No awk, capture o todo fileNna memória e processetarget_file

    awk '{ if(NR==FNR){
                         a[$1]=1
                      }else{
                         if(a[FNR]==1){ printf("%s","MARKER ")};
                         print 
                      }
         }' fileN target_file
    

    Ou, com uma variável para controlar quando a lista de arquivos com números de linha terminou:

    awk '{ if (dofile==1) {   if(a[FNR]==1){ printf("%s","MARKER ")};
                              print
                          }else{
                              a[$1]=1
                          }
         }' fileN fileK   dofile=1   target_file
    

    Observe que a última versão permite vários arquivos com números de linha, como fileNe fileKno exemplo.

    Observe também que as versões awk não processam números de linha repetidos. Todos os números de linha repetidos são processados ​​apenas uma vez.

    • 2
  5. Shawn
    2020-02-28T14:02:36+08:002020-02-28T14:02:36+08:00

    Outra maneira, usando edem vez de sedpara modificar o target_filelocal:

    (while IFS= read n; do echo "${n}s/^/MARKER/"; done < linenos; echo w) |  ed -s target_file
    
    • 1

relate perguntas

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve