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 / 686124
Accepted
FelixJN
FelixJN
Asked: 2022-01-13 14:04:36 +0800 CST2022-01-13 14:04:36 +0800 CST 2022-01-13 14:04:36 +0800 CST

Faça operações de alteração apenas na substring

  • 772

Em um arquivo que tenha algum texto ilegível antes e depois de uma seção marcada por padrões STARTe END(strings específicas que ocorrem apenas uma vez cada e na ordem correta e na mesma linha). Eu gostaria de fazer alguma manipulação de string SOMENTE na parte entre STARTeEND

Exemplo de entrada:

aomodi3hriq32| ¶³r 0q93aoiSTART_this_is_to_be_modified_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART_this_also_needs_modification_ENDqa 032/a237(°1Q"§ >A_this_
START changeme ENDnot_this_modias

Em termos de sed-operações, a substring (e apenas a substring) entre STARTe ENDdeve ser modificada como se eu usasse sed 's/_this_// ; s/modi/MODI/ ; y/as/45/'.

Saída de exemplo:

aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
START ch4ngeme ENDnot_this_modias

awkcom FS="START|END"falha, pois OFSnão pode ser definido para vários valores em posições diferentes.

Eu tentei usar sedcom uma substituição de comando aninhada e separadores diferentes ( ~), mas falhei e também temo que possa haver caracteres antes START/ depois ENDque mexam com o comando (por exemplo, a /). A ideia era selecionar apenas a substring "interna" e fazer as operações e usá-la como parte da substituição:

sed "s/^\(.*\)START.*END\(.*\)$/\1$(sed 's~^.*START~~
                                         s~END.*~~
                                         s~_this_~~
                                         s~modi~MODI~
                                         y~as~45~' infile)\2/" infile

Eu não estou familiarizado com, por exemplo perl.... mas o que for preciso.

Existe alguma maneira de fazer com que um conjunto de sed-operações se aplique apenas a uma substring correspondente a REGEX de uma linha?

awk text-processing
  • 8 8 respostas
  • 528 Views

8 respostas

  • Voted
  1. Best Answer
    choroba
    2022-01-13T14:39:48+08:002022-01-13T14:39:48+08:00
    perl -CSD -ne '
        if (my ($before, $between, $after) = /^(.*START)(.*)(END.*)/) {
            s/_this_//, s/modi/MODI/, tr/as/45/ for $between;
            print "$before$between$after\n";
        } else { print; }' -- file
    
    • -CSDdecodifica a entrada de UTF-8 e codifica a saída para UTF-8
    • Em vez de preencher as três variáveis $before​​, $between, e $after, poderíamos ter usado /pcom ${^PREMATCH}and ${^POSTMATCH}, mas não acho a solução mais agradável:
      if (my ($s) = /START(.*)END/p) {
          s/_this_//, s/modi/MODI/, tr/as/45/ for $s;
          print "${^PREMATCH}START${s}END${^POSTMATCH}";
      } else { print; }
      

    Se as partes START...END puderem ser repetidas em uma única linha, você precisará fazer um loop em cada linha.

    for my $part (split /(START.*?END)/) {
        if ($part =~ /^START.*END$/) {
            s/_this_//, s/modi/MODI/, tr/as/45/ for $part;
        }
        print "$part";
    }
    
    • 5
  2. they
    2022-01-14T00:39:07+08:002022-01-14T00:39:07+08:00

    Usando padrão sede assumindo que cada linha contém exatamente uma STARTe uma ENDsubstring (nessa ordem):

    # Skip (pass through) lines that does not have START followed by END.
    /.*START\(.*\)END.*/ !b
    
    # Save the original line in the hold space.
    h
    
    # Remove the start and the end from the line.
    # This leaves the bit of the line that we want to modify.
    # (This reuses the previous regular expression.)
    s//\1/
    
    # Modify what's left.
    s/_this_//
    s/modi/MODI/
    y/as/45/
    
    # Append the original line from the hold space,
    # with a newline as delimiter.
    G
    
    # Move the modified bit into the correct spot with a substitution,
    # while deleting the old substring between START and END.
    s/\(.*\)\n\(.*START\).*\(END.*\)/\2\1\3/
    

    Teste:

    $ cat file
    aomodi3hriq32| ¶³r 0q93aoiSTART_this_is_to_be_modified_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART_this_also_needs_modification_ENDqa 032/a237(°1Q"§ >A_this_
    START changeme ENDnot_this_modias
    
    $ sed -f script file
    aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
    START ch4ngeme ENDnot_this_modias
    

    Em linha, na linha de comando:

    sed -e '/.*START\(.*\)END.*/!b' -e h -e 's//\1/' \
        -e 's/_this_//' -e 's/modi/MODI/' -e 'y/as/45/' \
        -e G -e 's/\(.*\)\n\(.*START\).*\(END.*\)/\2\1\3/' file
    
    • 4
  3. FelixJN
    2022-01-13T14:17:05+08:002022-01-13T14:17:05+08:00

    Talvez com awke funções de string:

    awk 'BEGIN{FS="START|END"}
         /START.+END/ {gsub(/_this_/,"",$2)
         gsub(/modi/,"MODI",$2)
         gsub(/a/,"4",$2)
         gsub(/s/,"5",$2)
         print $1"START"$2"END"$3 ; next}
         1' infile
    
    • 3
  4. αғsнιη
    2022-01-13T16:54:32+08:002022-01-13T16:54:32+08:00

    Você sempre pode construir seu próprio OFS múltiplo:

    awk -v FS='START|END' -v OFS= -v map='_this_\r\rmodi\rMODI\ra\r4\rs\r5' '
      BEGIN{ split(FS, mOFS, "|") }
      { n=split(map, tr, "\r"); for(i=1; i<n; i+=2) gsub(tr[i], tr[i+1], $2);
      print $1, mOFS[1], $2, mOFS[2], $3
    }' infile
    

    note que o primeiro argumento do gsub() é o regex, então cuidado ao definir o map=....; também não deve haver alguns caracteres especiais para seu mapeamento à direita, como Ì &back-references \1, etc; no entanto, como você está escrevendo o mapeamento manualmente, você pode escapar quaisquer caracteres especiais para evitar que eles sejam interpretados especialmente pelo gsub().

    Eu usei CR \rpara separar o mapeamento como você mencionou que é a única coisa que não existirá em seu arquivo de entrada ao lado do \0qual isso não pode ser usado em split() e outras funções em awk (ou talvez em outras linguagens de programação também) como awk irá apenas considere o máximo que \0pode existir dentro de uma string. portanto, cada regex à esquerda tr[i](strings aqui) será substituída pela próxima à direita tr[i+1]da trmatriz.

    usar esta forma irá evitar que você escreva vários gsub() para cada par.

    • 3
  5. schrodingerscatcuriosity
    2022-01-13T15:25:08+08:002022-01-13T15:25:08+08:00

    Este GNU seddá o resultado desejado

    $ sed 's/\(.\)\(START\|END\)/\1\n\2\n/g' file | \
      sed -ne '/START/,/END/s/_this_//' \
      -ne '/START/,/END/y/as/45/' \
      -ne '/START/,/END/s/modi/\U&/g;p' | \
      sed -z 's/\n\(START\|END\)\n/\1/g'
    aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
    START ch4ngeme ENDnot_this_modias
    
    
    • 2
  6. Ed Morton
    2022-01-13T17:57:41+08:002022-01-13T17:57:41+08:00

    Usando qualquer awk em qualquer shell em cada caixa Unix:

    $ cat tst.awk
    match($0,/START.*END/) {
        tgt = substr($0,RSTART+5,RLENGTH-8)
        sub(/_this_/,"",tgt)
        sub(/modi/,"MODI",tgt)
        gsub(/a/,"4",tgt)
        gsub(/s/,"5",tgt)
        $0 = substr($0,1,RSTART+4) tgt substr($0,RSTART+RLENGTH-3)
    }
    { print }
    

    $ awk -f tst.awk file
    aomodi3hriq32| ¶³r 0q93aoiSTARTi5_to_be_MODIfied_ENDaqsdofuha23uru| ²23i ii3uhfia
    oawpo3<9"§ A hSTART4l5o_need5_MODIfic4tion_ENDqa 032/a237(°1Q"§ >A_this_
    START ch4ngeme ENDnot_this_modias
    
    • 2
  7. Olivier Dulac
    2022-01-14T06:59:02+08:002022-01-14T06:59:02+08:00

    Apresento uma solução que também irá

    • trabalhe apenas entre um START e END, o que estiver no meio (mas SOMENTE se não houver START ou END no meio)
    • funciona mesmo que o intervalo entre várias linhas

    restrição : suponho que seu arquivo não use 4 caracteres, escolhi o 'frequentemente não utilizado' "\001" a "\004" (mas quaisquer outros 4 caracteres não utilizados podem ser usados)

    (como eu uso: \001 para fazer qualquer END começar com uma nova linha e qualquer END terminar com uma nova linha, forçando qualquer outra combinação que não seja "START(nonSTARTnorEND)END" a estar em linhas separadas e, portanto, não considerada. e eu uso \ 004 para "salvar" as novas linhas do arquivo original e recuperá-las no final. E eu uso \002 para representar um START, \003 para representar um END, permitindo-me verificar se não há nenhum desses entre também (e que Começo com START e termino com END quando procuro as strings a serem substituídas.) Todas essas coisas são permitidas por causa dessas substituições.

    Alguém poderia fazer:

    sed -e "s/START/$(printf '\001\002')/g" -e "s/END/$(printf '\003\001')/g" INPUT \
    | tr '\001\n' '\n\004' \
    | gawk '
      /^\002[^\002\003]*\003$/ {
        # we know we are STICTLY between a START(\002) and an END(\003), with none of them occuring inside
        gensub("_this_", "", "g", $0) # remove inbetween START&END all occ. of _this_
        gensub("a", "4", "g", $0) ; gensub("s", "5", "g", $0) # "a" -> "4", "s" -> "5"
        gensub("modi", "MODI", "g", $0)
      }
      1 # print every lines
     ' \
    | tr '\n\004' '\001\n' \
    | tr -d '\001' \
    | sed -e "s/$(printf '\002')/START/g" -e "s/$(printf '\003')/END/g" > OUTPUT
    

    nota: isso pode ser ainda mais simplificado (não é necessário substituir START por \002 nem terminar por \003, eu fiz isso primeiro para poder usar : [^\002\003]*para garantir que a string intermediária também não contenha, mas o \001-> \n garante que já...)

    • 1
  8. guest_7
    2022-01-15T21:34:43+08:002022-01-15T21:34:43+08:00

    Você pode fazer o que estava tentando, desde que tenha GNU sed com o sinalizador /e no comando s///:

    sed -Ee "
      s/'/&\\\\&&/
      s/(.*START)(.*)(END.*)/printf %s '\\1' \"\$(printf '%s\\\\n' '\\2'|sed -e 's:_this_::;s:modi:MODI:;y:as:45:')\" '\\3'/e
    " infile
    

    O acima pode ser dividido em funções para torná-lo mais limpo. Aqui definimos funções auxiliares e variáveis ​​para desorganizar:

    xform() {
      printf '%s\n' "$1" |
      sed -e '
        s/_this_//
        s/modi/MODI/
        y/as/45/
      '
    }
    
    fx() {
      printf %s "$1" "$(xform "$2")" "$3"
    }
    
    export -f fx xform
    
    bre=$(printf '\\(%s\\)'  '.*START' '.*' 'END.*')
    
    sed -e "
      s/'/&\\\\&&/g
      s/$bre/fx '\\1' '\\2' '\\3'/e
    " infile
    

    Com Perl , isso vem naturalmente:

    perl -lpe '
      s{(?<=START)(.*?)(?=END)}
       [
         local $_=$1;
         s/_this_//;
         s/modi/MODI/;
         tr/as/45/r;
       ]e;
    ' infile
    

    Ou, POSIXly podemos particionar o espaço do molde em 3 partes, armazenar em espera, então transformar a parte do meio e costurá-las de volta.

    sed -e '
      s/\n.*//;ta
      s/START.*END/\
    &\
    /;h;D;:a
      s/_this_//;s/modi/MODI/;y/as/45/
      G;s/\(.*\)\n\(.*\)\n.*\n/\2\1/
    ' infile
    
    • 0

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