Como faço para o iconv substituir o arquivo de entrada pela saída convertida?

70

Eu tenho um script bash que enumera todos os arquivos * .php em um diretório e se aplica iconva ele. Isso obtém saída em STDOUT.

Como adicionar o -oparâmetro (na minha experiência) realmente grava um arquivo em branco provavelmente antes da conversão, como posso ajustar meu script para que ele faça a conversão e substitui o arquivo de entrada?

for file in *.php
do
    iconv -f cp1251 -t utf8 "$file"
done
meder omuraliev
fonte
Veja também Aviso sobre ">" .
G-Man

Respostas:

76

Isso não está funcionando porque iconvprimeiro cria o arquivo de saída (já que o arquivo já existe, ele o trunca) e depois começa a ler o arquivo de entrada (que agora está vazio). A maioria dos programas se comporta dessa maneira.

Crie um novo arquivo temporário para a saída e mova-o para o lugar.

for file in *.php
do
    iconv -f cp1251 -t utf8 -o "$file.new" "$file" &&
    mv -f "$file.new" "$file"
done

Se a sua plataforma iconvnão tiver -o, você poderá usar um redirecionamento de shell para o mesmo efeito.

for file in *.php
do
    iconv -f cp1251 -t utf8 "$file" >"$file.new" &&
    mv -f "$file.new" "$file"
done

O spongeutilitário de Colin Watson (incluído no moreutils de Joey Hess ) automatiza isso:

for file in *.php
do
    iconv -f cp1251 -t utf8 "$file" | sponge "$file"
done

Esta resposta se aplica não apenas a, iconvmas a qualquer programa de filtro. Vale a pena mencionar alguns casos especiais:

  • O GNU sed e o Perl -ptêm uma -iopção para substituir os arquivos no local.
  • Se o arquivo for muito grande, o filtro só é alterar ou remover algumas partes, mas nunca adicionando coisas (por exemplo grep, tr, sed 's/long input text/shorter text/'), e você gosta de viver perigosamente, você pode querer realmente modificar o arquivo no local (as outras soluções mencionadas aqui criar uma novo arquivo de saída e mova-o para o lugar no final, para que os dados originais permaneçam inalterados se o comando for interrompido por qualquer motivo).
Gilles 'SO- parar de ser mau'
fonte
3
Não tenho muita certeza se a autoria de spongedeve ser atribuída exclusivamente a Joey Hess; é o pacote moreutilsque spongeele mantém, mas no que diz respeito à origem sponge, seguindo os links da página inicial de moreutils, eu o achei originalmente publicado e sugerido para inclusão por Colin Watson: "Joey escreve sobre a falta de novas ferramentas que se encaixam na filosofia do Unix. Meu favorito de tais coisas que escrevi é sponge"(Seg, 06 de fevereiro de 2006).
imz - Ivan Zakharyaschev 30/03
3
Eu uso o Mac OS, não há opção -o no iconv, preciso alterar `iconv -f cp1251 -t utf8 -o" $ file.new "" $ file "` paraiconv -f cp1251 -t utf8 "$file" > "$file.new"
code4j
Alguns comandos, como sort, são bastante inteligentes em relação aos -oparâmetros, e se eles detectam o arquivo de saída é o mesmo que a entrada, eles gerenciam internamente um arquivo temporário, para que funcione.
Jsjimher
56

Uma alternativa é recode, que usa a biblioteca libiconv para algumas conversões. Seu comportamento é substituir o arquivo de entrada pela saída, portanto, isso funcionará:

for file in *.php
do
    recode cp1251..utf8 "$file"
done

Como recodeaceita vários arquivos de entrada como parâmetro, você pode poupar o forloop:

recode cp1251..utf8 *.php
homem a trabalhar
fonte
2
Obrigado, isso merece mais votos. Basta saber onde se olhou no manual sobre os 2 pontos entre as codificações ...
neurino
2
“O PEDIDO geralmente se parece com ANTES ... DEPOIS, com ANTES e DEPOIS sendo charsets.” Esse manual é realmente difícil de seguir com todos esses pontos duplos (que fazem parte da sintaxe) e pontos triplos (o que significa mais disso). Um conselho: tente info recode. É mais detalhado.
manatwork
4

Para agora

find . -name '*.php' -exec iconv -f CP1251 -t UTF-8 {} -o {} \;

Funciona como um encanto

galeksandrp
fonte
5
No começo, eu realmente pensei que funciona. Mas parece que a saída superior a 32K foi cortada e, com ainda mais entradas, dispara os dumps do núcleo.
X-yuri
1

Você pode usar o Vim no modo Ex:

ex -sc '%!iconv -f cp1251 -t utf8' -cx "$file"
  1. % selecione todas as linhas

  2. ! comando de execução

  3. x salvar e fechar

Steven Penny
fonte
0

Aqui está um exemplo simples . Deve fornecer informações suficientes para começar.

#!/bin/bash
#conversor.sh
#Author.....: dede.exe
#E-mail.....: [email protected]
#Description: Convert all files to a another format
#             It's not a safe way to do it...
#             Just a desperate script to save my life...
#             Use it such a last resort...

to_format="utf8"
file_pattern="*.java"

files=`find . -name "${file_pattern}"`

echo "==================== CONVERTING ===================="

#Try convert all files in the structure
for file_name in ${files}
do
        #Get file format
        file_format=`file $file_name --mime-encoding | cut -d":" -f2 | sed -e 's/ //g'`

        if [ $file_format != $to_format ]; then

                file_tmp="${unit_file}.tmp"

                #Rename the file to a temporary file
                mv $file_name $file_tmp

                #Create a new file with a new format.
                iconv -f $file_format -t $to_format $file_tmp > $file_name

                #Remove the temporary file
                rm $file_tmp

                echo "File Name...: $file_name"
                echo "From Format.: $file_format"
                echo "To Format...: $to_format"
                echo "---------------------------------------------------"

        fi
done;
dede.exe
fonte
0
echo "`iconv -f cp1251 -t utf8 $file`" > "$file"

funciona para mim

CoNsTaR
fonte
0

Você pode usar find, pelo menos isso funcionou para mim no Raspbian Stretch:

find . -type f -name '*php' -execdir iconv -f cp1251 -t UTF-8 '{}' -o '{}'.tmp \; -execdir mv '{}'.tmp '{}' \;
rannala
fonte
0

Uma opção é usar perla interface iconve o -imodo para edição no local:

perl -MText::Iconv -i -pe '
  BEGIN{$i=Text::Iconv->new(qw(cp1252 UTF-8));$i->raise_error(1)}
  $_ = $i->convert($_)' ./*.php

Com o GNU awk, você também pode fazer algo como:

gawk -v cmd='iconv -f cp1252 -t utf-8' -i inplace '
  {print | cmd}; ENDFILE {close(cmd)}' ./*.php

O ksh93shell também possui um >;operador para o que armazena a saída em um arquivo temporário que é renomeado para o arquivo redirecionado se o comando for bem-sucedido:

for f in *.php; do
  iconv -f cp1252 -t utf-8 < $f >; $f
done
Stéphane Chazelas
fonte