Como posso alterar o código ^ L em muitos arquivos no Ubuntu?

8

Eu tenho muitos arquivos XML, mais de 50000 deles.

Em alguns arquivos XML, alguns arquivos são gravados assim

<filename>abc.JPEG<^Lilename>

^Lé apenas um caractere, mas não consigo encontrar o que ^Lsignifica com o Google.

Quando eu uso catpara imprimir o conteúdo de um arquivo, ele aparece como o seguinte

<filename>abc.JPEG<
                   ilename>

De qualquer forma, quero mudar <filename>abc.JPEG<^Lilename>para<filename>abc.JPEG</filename>

Eu já encontrei algum comando para alterar uma palavra em muitos arquivos, como

find . -exec perl -pi -e 's/[find_word]/[change_word]/g' {} \;

Mas esse comando não funciona no meu caso, porque não pode reconhecer a palavra de pesquisa quando eu apenas digito ^L.

Como posso mudar <filename>abc.JPEG<^Lilename>para <filename>abc.JPEG</filename>em muitos arquivos?

Yang
fonte
6
Aparentemente, alguém usou em <\filename>vez de </filename>em um contexto em \fque seria interpretado como o caractere de feed de formulário. Você provavelmente deve rastrear a origem desses arquivos e apontar o problema com a ferramenta de geração para o desenvolvedor. Para corrigir os arquivos, a resposta aceita é ótima.
Hans-Martin Mosner 5/08/19

Respostas:

17

Control-L (representado como ^L) é o caractere "feed de formulário". No ASCII, ele possui o valor decimal 12 ( Lé a 12ª letra do alfabeto) ou o valor hexadecimal 0c:

$ printf 'foo\x0cbar\n' | cat -et
foo^Lbar$

$ printf 'foo\x0cbar\n'
foo
   bar

Você pode substituí-lo usando ferramentas como sed especificando o código de escape hexadecimal:

$ printf 'foo\x0cbar\n' | sed 's/\x0c//'
foobar

Como alternativa, componha ^Ldiretamente usando a sequência do teclado CTRL+ V CTRL+L

sed 's/CTRL+VCTRL+L//'

Para sua substituição específica, dado

$ printf '<\x0cilename\n'
<
 ilename

então

$ printf '<\x0cilename\n' | sed 's/<\x0c/<\/f/g'
</filename

(o gmodificador é adicionado caso haja mais de uma instância por linha).

chave de aço
fonte
No meu caso, "$ printf '<\ x0 nome_do_conto \ n' | sed 's / <\ x0c / <\\ f / g'" não está funcionando. Mas, de acordo com sua resposta, "$ find. -Exec perl -pi -e 's / <\ x0cilename> / <\ / filename> / g' {} \;" funciona bem. Obrigado pela sua resposta :)
Yang
@Yang desculpe, eu só percebi que eu confuso barra e barra invertida na minha resposta (corrigido agora) - ainda não sei por que teria impedido a versão sed de trabalhar embora
steeldriver
Uma resposta muito boa! Seria ainda melhor se incluísse, digamos, um findloop sobre esses 50000 arquivos XML e processasse automaticamente cada um (e fizesse um backup também).
Kingsley
2

Como Hans-Martin Mosner aponta nos comentários, parece que alguém usou barras invertidas em vez de barras ao gerar o XML (ou possivelmente executou toda a <filename>seção através de um conversor Unix para Windows que era muito zeloso em relação a barras). \fé uma sequência de escape raramente usada para um caractere de avanço de formulário, também conhecido como U + 0C ou ^ L. Portanto, alguma etapa posterior do pipeline substituiu os \fcaracteres U + 0C literais.

Felizmente, U + 0C é um caractere extremamente raro que dificilmente será encontrado intencionalmente em qualquer tipo de XML. E uma vez que apenas \fproduziria isso, em oposição a (digamos) \gou \k, uma busca e substituição universal deve corrigir não apenas, </filename>mas também </folder>,</file> ou qualquer outra coisa que ficou mutilado.

É isso que o sed-script da steeldriver faz; Eu apenas tornaria isso um pouco mais geral:

sed 's|\x0c|/f|g'

Isso significa que "(s) trocamos todas as instâncias de \x0c(isto é, U + 0C) para /f, (g) globalmente".

Draconis
fonte
2

\fé o caractere de feed de formulário no Perl. Parece que esses arquivos malformados foram criados por alguém novo no Perl e no XML.

Aqui está uma correção muito Perlier - que também atende aos objetivos do OP de automatizar a atualização de todos os arquivos, ao contrário da resposta aceita com sed, que só funcionará em um arquivo por vez, pois não está emparelhado com find .

\fpode simplesmente ser empregado em vez do código hexadecimal x0c.

find . -type f -exec perl -pi.bkp -e 's [ \f ilename ][ /f ilename ]gx' {} \;

Aqui eu adicionei -type fao tel findpara retornar apenas arquivos simples - caso contrário find, retornará .à lista e acionará um aviso quando você tentar editá-lo, embora tudo o mais ainda funcione.

Também facilitei a visualização do regex usando a xflag que ignora o espaço em branco real, permitindo espaçar os elementos do seu regex. Se você não gosta disso, aqui está sem:

find . -type f -exec perl -pi.bkp -e 's[\filename][/filename]g' {} \;

E, no provável caso de todos os caracteres de feed de formulário serem falsos e todos serem substituídos por /f, você pode reduzir ainda mais a linha única:

find . -type f -exec perl -pi.bkp -e 's[\f][/f]g' {} \;

Você não precisa usar barras para cercar os elementos do comando de substituição de expressões regulares ( s///) no Perl. Você pode usar qualquer símbolo. Se você optar por usar qualquer tipo de símbolo parecido com colchete, no entanto, precisará usar os dois: s[old][new]por exemplo.

Como não estou usando barras, não preciso escapar de nenhuma barra.

Quanto a -i.bkp: perl -pi -epermite editar no local - mas se você quiser um seguro extra, caso o programa Perl encontre e substitua errado, você pode colocar uma extensão de arquivo para que ele faça uma cópia dos arquivos originais para você. Aqui, eu usei .bkp.

Nas versões mais recentes do Perl, a edição no local foi atualizada para ser mais resiliente, caso o seu sistema sofra um problema sério, como perda de energia ou falta de espaço em disco também. Aqui está o autor do Perl, Brian Doye, sobre a edição aprimorada no local nos Perls recentes.

Você deve considerar o uso de Perl para esses tipos de tarefas, porque é uma linguagem de programação de uso geral extremamente poderosa, porém subestimada, cujo objetivo original do projeto era substituir sede awkpor algo muito melhor.

Os recursos de correspondência de regex do Perl 5 e a sintaxe aprimorada de regex excedem em muito os de sed,awk e de fato qualquer outra linguagem de programação além do Perl 6, tornando o Perl a escolha mais sensata para manipulações simples e avançadas de regex.

Para esclarecer: sedtambém funcionará bem finde você também pode sed -i.bkpfazer um backup de cada arquivo editado, mas até onde eu sei, ele não apresenta a resiliência extra no Perl 5.28 e posterior. Ele também usa a sintaxe de regex UNIX® tradicional, muito mais barulhenta e menos poderosa.

Medlock Perlman
fonte