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 / 668793
Accepted
ChennyStar
ChennyStar
Asked: 2021-09-14 01:54:21 +0800 CST2021-09-14 01:54:21 +0800 CST 2021-09-14 01:54:21 +0800 CST

sed : tem uma faixa terminando com a última ocorrência de um padrão (faixa gananciosa)

  • 772

Pegue o seguinte arquivo:

$ cat f1
stu vwx yza
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu
pqr stu vwx
stu vwx yza

Para imprimir todas as linhas desde a primeira que contém abcaté a primeira que contém mnocom GNU sed:

$ sed -n '/abc/,/mno/p' f1
uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno

Como eu poderia imprimir todas as linhas até a última contendo mno, por exemplo, como eu poderia obter o seguinte resultado:

uvw xyz abc
abc def ghi
def ghi jkl
ghi jkl mno
jkl mno pqr
mno pqr stu

Em outras palavras, existe uma maneira de tornar seda seleção de intervalo do GNU gananciosa?

Atualizar

Na minha configuração:

  • Se mnoestiver faltando, deve imprimir tudo até o final do arquivo.
  • mnonão pode ocorrer antes do primeiro abc.
  • Sempre há pelo menos um abce nunca estão abcna mnomesma linha

EDIT Acabei de adicionar uma stu vwx yzalinha fictícia no início, para que o arquivo não comece com uma linha incluindo abc(para evitar soluções que começam na primeira linha - elas devem começar na primeira linha abc)

awk text-processing
  • 9 9 respostas
  • 1092 Views

9 respostas

  • Voted
  1. Inian
    2021-09-14T02:33:42+08:002021-09-14T02:33:42+08:00

    Você poderia usar awkse for uma opção. Você pode marcar as linhas para o início e a parada do padrão e imprimir essas linhas em uma passagem do arquivo (envolve o armazenamento de linhas a partir da primeira linha contendo abcaté a última linha em um buffer)

    awk '/abc/ && !start {
      start = NR
    }
    /mno/ {
      stop = NR
    }
    start { line[NR] = $0 }
    END {
      if ( !stop ) {
        stop = NR
      }
      for ( s = start; s <= stop; s++ )
        print line[s]
    }' file
    

    Observe que isso não funcionará, quando o padrão inicial não estiver presente, imprimindo apenas uma série de linhas em branco.

    • 5
  2. Best Answer
    nezabudka
    2021-09-14T03:51:40+08:002021-09-14T03:51:40+08:00
    sed '/abc/,$!d;0,/mno/b;:1;/mno/b;$d;N;b1' file
    

    Algoritmo de trabalho:
    Dois intervalos de endereços são usados.
    O primeiro /abc/,$!d;remove tudo até a primeira correspondência de padrão.
    O segundo 0,/mno/b;até uma correspondência com o padrão /mno/, envia cada buffer de linha (espaço do padrão) para a saída ignorando o script restante, evitando assim a exclusão caso o padrão não seja encontrado no arquivo.
    O resto do script :1;/mno/b;$d;N;b1funciona em um loop. No buffer do editor, as linhas são anexadas até que ocorra uma correspondência de padrão. Se um /mno/padrão for encontrado, todo o buffer será enviado para a saída, ignorando o restante do script. Se nenhuma correspondência ocorrer, o buffer será excluído na última linha.

    • 5
  3. AdminBee
    2021-09-14T03:18:27+08:002021-09-14T03:18:27+08:00

    Outra awksolução com menos buffering:

    awk '!f&&/abc/{f=1} f==1; f==2{buf=buf $0 ORS} f&&/mno/{f=2; printf "%s",buf; buf=""}' input.txt
    
    • Isso imprimirá tudo, começando com a primeira ocorrência de abc(onde define um sinalizador fcomo 1) até e incluindo a primeira ocorrência de mno. A f==1instrução fora dos blocos de regras instrui awka imprimir a linha atual desde que festeja definida como 1.
    • Em seguida, o conteúdo de todas as linhas após cada ocorrência de mno(onde fagora tem o valor 2) é armazenado em um buffer buf, que é impresso e limpo na próxima ocorrência de mno. Para garantir que lidamos corretamente com situações em que o first mnoocorre antes do first abc, exigimos que fseja definido pelo menos 1 antes de aplicar essa lógica.

    Portanto, armazenará no máximo o texto entre duas ocorrências de mno, ou a última ocorrência de mnoe fim de arquivo (só que a última parte nunca será impressa).

    Se você deseja trocar velocidade com eficiência de memória, o método de duas passagens a seguir não dependerá de buffer:

    awk 'FNR==NR{if (/abc/&&!start) {start=FNR} else if (/mno/) {end=FNR}; next} FNR>=start&&(!end||FNR<=end)' input.txt input.txt
    

    Isso processará o arquivo duas vezes (portanto, é especificado duas vezes como argumento).

    • Na primeira vez, quando FNR, o contador de linhas por arquivo é igual a NR, o contador de linhas global, procuramos a primeira ocorrência de abce a última ocorrência de mno, e armazenamos seus números de linha em starte end, respectivamente.
    • Na segunda passagem, imprimimos linhas desde que o FNRcontador esteja entre (e incluindo) o startfinal end(ou simplesmente se for maior/igual do que startse endnão estiver definido).
    • 4
  4. terdon
    2021-09-14T02:08:56+08:002021-09-14T02:08:56+08:00

    Eu não acho que o sed pode ser ganancioso, não. Uma possível solução alternativa, simples mas ineficiente, seria processar o arquivo duas vezes. Uma para obter o intervalo de linhas e outra para imprimir. Por exemplo:

    $ perl -lne '$s||=$. if /abc/; $e=$. if /mno/; }{ print "$s $e"' file | 
        while read start end; do sed -n "$start,${end}p" file; done
    abc def ghi
    def ghi jkl
    ghi jkl mno
    jkl mno pqr
    mno pqr stu
    

    Como alternativa, se você quiser lidar com casos em que um ou ambos os padrões estão ausentes:

    perl -lne '$s||=$. if /abc/; $e=$. if /mno/; }{ $s||=1; $e||=$.; print "$s $e"' file | 
        while read start end; do sed -n "$start,${end}p" file; done
    

    Se abcnão for encontrado, imprimirá a partir do início do arquivo. Se mncnão for encontrado, imprimirá do abc(ou do início, se abcnão estiver lá) até o final. Se nenhum padrão for encontrado, é claro que não imprimirá nada.

    • 3
  5. Philippos
    2021-09-14T02:31:50+08:002021-09-14T02:31:50+08:00

    Colete linhas até que uma mnosequência as deixe prontas para impressão:

    sed -e '/abc/,$!d;:loop' -e'/mno/{p;d;}' -e '$d;N;bloop'
    
    • /abc/,$!d dexclui tudo, exceto o intervalo da primeira abclinha até o final. Isso também lida com o caso quando não há nenhum abc.
    • Então precisamos :loop.
    • /mno/{p;d;}se houver mnono espaço do padrão, print e comece de novo.
    • $dse chegarmos à última linha sem mno, delege tudo no buffer. Infelizmente, isso significa que não há saída, se não houver mno.
    • Caso contrário, acrescente a Nlinha ext e continue o loop.
    • 2
  6. Philippos
    2021-09-14T05:02:57+08:002021-09-14T05:02:57+08:00

    Você pode coletar todas as linhas, começando pela abclinha no espaço de espera e, em seguida, usar a natureza gananciosa de .*para excluir tudo após o último mno:

    sed '/abc/,$!d;H;$!d;x;s/\n//;s/\(.*mno[^\n]*\).*/\1/'
    
    • /abc/,$!dé ddeletar tudo antes da primeira abclinha (ou o arquivo inteiro, se não houver nenhuma abclinha)
    • H;$!dé o padrão clássico para coletar o arquivo inteiro no espaço de espera (observe que isso pode ser um problema para arquivos muito grandes)
    • mudamos os xbuffers em vez de usar gpara evitar copiar um buffer grande
    • s/\n//remove a nova linha errada no início, produzida acrescentando ao espaço de espera vazio
    • s/\(.*mno[^\n]*\n\).*/\1/remove tudo após a última mno linha (ou imprime todo o arquivo restante, se não houver mnolinha, conforme solicitado). Observe que [^\n]não é POSIX e funcionará apenas em algumas versões como GNU sed.
    • 2
  7. jubilatious1
    2021-09-15T14:58:18+08:002021-09-15T14:58:18+08:00

    Usando Raku (anteriormente conhecido como Perl_6)

    raku -e '(S:g/ <( ^ .*? $$ \n )> ^^ .*? abc .*? $$ // andthen S:g/ ^^ .* mno .*? $$  <( .*? $)> //).put for lines.join("\n");'
    

    Entrada de amostra:

    1. stu vwx yza
    2. uvw xyz abc
    3. abc def ghi
    4. def ghi jkl
    5. ghi jkl mno
    6. jkl mno pqr
    7. mno pqr stu
    8. pqr stu vwx
    9. stu vwx yza
    10. mno pqr stu
    11. xyz xyz xyz
    

    Saída de amostra:

    2. uvw xyz abc
    3. abc def ghi
    4. def ghi jkl
    5. ghi jkl mno
    6. jkl mno pqr
    7. mno pqr stu
    8. pqr stu vwx
    9. stu vwx yza
    10. mno pqr stu
    

    Observe acima que a Saída de Amostra é um retorno das linhas de 2 a 10 quando alimentada a Entrada de Amostra de 11 linhas. Além disso, quando a entrada de amostra é truncada apenas para as linhas de 1 a 10 (ou seja, com mnona última linha), o código Raku acima ainda (corretamente) retorna as linhas de 2 a 10.

    Obrigado a @ImHere e @ChennyStar por me estimular nos comentários a apresentar uma solução Raku mais robusta.

    https://raku.org

    • 1
  8. guest_7
    2021-09-16T16:15:49+08:002021-09-16T16:15:49+08:00

    Usando GNU sed Nota: a primeira linha abc não tem mno conforme OP, então podemos explorar esse fato no código sed abaixo.

    sed -e '
      /abc/,$!d
      /mno/{h;b;}
      $!{N;s/^/\n/;D;}
      x;/./d;x
    ' file
    

    Neste método, usamos o modo slurp -zpara ler o arquivo completo no espaço de padrões. Em seguida, excluímos até antes da primeira linha contendo abc. Depois disso, alcance a última linha mno usando a ganância do regex.

    sed -Ez '
      s/abc/\x0&/
      s/.*\n(.*)\x0/\1/
      s/(.*mno[^\n]*\n).*/\1/
    ' file
    

    Ainda outra maneira é uma abordagem de duas passagens, onde registramos os números de linha da primeira linha abc e da última linha mno. Caso não haja mno presente, preenchemos $ em seu lugar. Então, usando esses dois números, construímos um comando sed begin,end p;endq

    sed -n '/abc/{=;:a;n;/mno/=;ba}' file |
    sed -En '
      1{h;$s/.*/$/;}
      ${x;G;}
      s/\n(.*)/,\1p&q/p
    ' | sed -nf - file
    

    Podemos usar perlpara slurp o arquivo e, em seguida, o arquivo inteiro é uma longa string que queimamos de ambas as extremidades e paramos quando nossas condições são atendidas.

    perl -0777 -pe '
      s/^.*\n// until /^.*abc/;    /mno/||next;
      s/.*\n$// until /mno.*$/;
    ' file
    
    • 1
  9. guest_7
    2021-09-18T05:53:47+08:002021-09-18T05:53:47+08:00

    Aqui estão mais maneiras de usar o editor sed para obter a saída desejada.

    sed -n '
      /\n/{/mno/!d;P;D;}
      /abc/,$H;$!d
      z;x;G;/mno/D
      s/.//;s/.$//p
    ' file
    
    • Salve o arquivo do primeiro /abc/ até o final no espaço de espera.
    • imprima o topo do espaço do padrão enquanto ainda podemos ver /mno/ em qualquer lugar nele.
    • Em seguida, recorte a parte superior do espaço do padrão e repita a etapa anterior.
    • Pare quando /mno/ não estiver mais visível.
    • Alternativamente, antes de iniciar este ciclo P;D, se não houver /mno/, basta imprimir todo o espaço de espera.

    Outro método em que armazenamos linhas em espera apenas até que /mno/ seja visto. Nesse ponto, viramos e imprimimos o que estava em espera.

    sed -n '
      /abc/,$!d
      /mno/!{H;ba;}
      x;p;:a
      ${x;//P;//!p}
    ' file | sed 1d
    

    Aqui está a maneira do Python usando o método groupby do módulo itertools versátil para fazer o trabalho.

    python3 -c 'import sys, itertools as it
    ifile,start,stop = sys.argv[1:]
    G,K,F = [],[],lambda x: x.find(stop)
    with open(ifile) as f:
      for _ in f:
        if not _.find(start): continue
        for t in it.groupby(f,F):
          G.append(list(t[1]))
          K += [t[0] > -1]
    if len(K) > 1 and not K[-1]: G.pop()
    print(*[e for L in G for e in L], sep="",end="")
    ' file "abc" "mno"
    
    • 1

relate perguntas

  • Reorganize as letras e compare duas palavras

  • Subtraindo a mesma coluna entre duas linhas no awk

  • Embaralhamento de arquivo de várias linhas

  • como posso alterar o caso do caractere (de baixo para cima e vice-versa)? ao mesmo tempo [duplicado]

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