Como posso manipular o conteúdo de um arquivo duplicando e alterando algumas partes?

2

Eu quero manipular um arquivo de texto. Ele contém muitos blocos da forma

CLASS
   ...some stuff ...
END

Eu quero duplicar cada um desses blocos e adicionar e remover uma linha do seu conteúdo. Posso roteirizar isso?

    
por Mnementh 16.11.2010 / 15:28

4 respostas

2

As ferramentas naturais para isso são o awk e o Perl (supondo que você queira fazer o script: para uma vez, a ferramenta natural é um editor interativo). Aqui está um script awk que duplica todos os CLASSEND blocks (nenhum balanceamento suportado: cada CLASS corresponde ao próximo END ), exceto que foo lines são omitidos da segunda cópia.

awk '
  /^CLASS$/ { store = 1; }  # start storing
  store && ! /^foo$/ { hold = hold ORS $0; }  # if storing, maybe save line
  /^END$/ {
      $0 = $0 hold;  # append hold copy to current line
      store = 0; hold = "";  # end of block
  }
  1 { print; }  # print original line, with hold prepended if at end of block
'

Aqui está uma solução sed; não leve muito a sério. Não espere que ele se comporte se as linhas CLASS / END não estiverem em uma alternância estrita.

sed -e '/^CLASS$/,/^END$/!b' \
    -e '/^CLASS$/{' -e 'h' -e 'b' -e '}' \
    -e '/^foo$/!H' \
    -e '/^END$/G'
    
por 16.11.2010 / 20:24
3

Bem, você pode criar scripts para qualquer coisa.

No entanto, se eu estivesse fazendo isso, usaria vim macros. (Se você não está familiarizado com vim , você deve aprender, mas caso contrário, ignore isso)

Alguns ponteiros básicos:

q - inicia a gravação de uma macro

qx - pare de gravar uma macro e chame-a de 'x'

@x - execute a macro chamada 'x'

Uma macro re-executará imediatamente os comandos que você digitou durante a sessão de gravação.

Então, se você quiser que uma macro duplique um bloco como esse tipo:

q          start macro
/CLASS     jump to next "CLASS"
V          start selecting full lines
/END       jump to next "END
Y          yank selected lines (yank = copy)
j          scroll down one line
p          put (paste)
qd         stop recording and name the macro d

Há muitos bons recursos e exemplos disponíveis para uso no vim e macros vim.

Por que vale a pena, o sed é uma espécie de versão de linha de comando do vim, então você pode adaptar o que eu disse ao sed, mas estou menos familiarizado com ele, então não posso garantir .

~~ edit ~~

Outra coisa que torna isso mais útil é a capacidade de vim de abrir vários arquivos. Vários arquivos abertos são chamados de buffers. Você ativa isso tendo vários nomes de arquivos após vim (IE vim *.txt para abrir todos os arquivos txt)

Para executar uma macro em vários buffers, primeiro abra todos os arquivos ao mesmo tempo. Em seguida, use :bufdo normal @x para executar a macro x em cada buffer.

Note que você deve ter :w como o último passo da macro para evitar ficar preso quando tentar mudar para o próximo buffer se o atual não for salvo.

:bn será levado manualmente para o próximo buffer.

Além disso, consulte o link para saber como executar uma macro para cada linha. Misture e combine:)

    
por 16.11.2010 / 18:12
1

A ferramenta exata a ser usada dependerá das especificidades do trabalho. Se o trabalho só vai acontecer uma vez, você pode considerar o uso de uma macro em vim ou emacs . Se você realmente deseja fazer o script, eu procuraria sed :

Sed is a stream editor. A stream editor is used to perform basic text transformations on an input stream (a file or input from a pipeline).

Novamente, a maneira exata de fazer isso dependerá da sua situação, mas para adicionar uma linha depois de uma linha que começa com PATTERN, você pode fazer isso:

sed -e 's/\(^PATTERN.*\)/\nTextToAdd/' file.old > file.new

Para adicionar uma linha antes de uma linha que começa com PATTERN, você pode fazer algo assim:

sed -e 's/\(^PATTERN.*\)/TextToAdd\n/' file.old > file.new

Para remover uma linha que começa com PATTERN

sed -e 's/\(^PATTERN.*\)//' file.old > file.new

O truque será encontrar o PADRÃO correto que irá capturar apenas as linhas que você deseja.

Você pode combinar isso fazendo algo assim:

sed -e 's/\(^PATTERN1.*\)/\nTextToAdd/' -e 's/\(^PATTERN2.*\)/TextToAdd\n/' file.old > file.new
    
por 16.11.2010 / 19:36
1

E a solução Python minha:

file = open('a.txt', 'r')
arr = []
tmp = []
remember = False

for line in file:
    if "CLASS" in line:
        remember = True     # start storing lines
        tmp.append(line)    # we're storing lines in list
        continue            # go to next line
    if "END" in line:
        tmp.append(line)    # store line
        remember = False    # stop storing lines
        arr.append(tmp)     # add line to list
        tmp = []            # clear temp list
        continue            # go to next line
    if remember:
        tmp.append(line)    # add to list line between CLASS and END
file.close()

file = open('a.txt', 'a')
for tmp in arr:
    if '   ...some stuff ...1\n' in tmp:    # we can skip 
        continue                            # whole block if we find this
    for i in tmp:
        if "333" in i:  # we can skip only one line 
            continue
        file.write(i)
file.close()

Eu acho que é mais fácil de entender e modificar do que a versão awk (se você não sabe awk) ... Btw awk é a próxima língua na minha lista de idiomas que eu quero aprender ...;)

    
por 16.11.2010 / 20:58