¿Cómo puedo hacer que iconv reemplace el archivo de entrada con la salida convertida?

70

Tengo un script bash que enumera a través de cada archivo * .php en un directorio y se aplica iconva él. Esto obtiene salida en STDOUT.

Dado que agregar el -oparámetro (en mi experiencia) en realidad escribe un archivo en blanco probablemente antes de que tenga lugar la conversión, ¿cómo puedo ajustar mi script para que realice la conversión y luego sobrescribe el archivo de entrada?

for file in *.php
do
    iconv -f cp1251 -t utf8 "$file"
done
meder omuraliev
fuente
Consulte también Advertencia sobre ">" .
G-Man el

Respuestas:

76

Esto no funciona porque iconvprimero crea el archivo de salida (dado que el archivo ya existe, lo trunca), luego comienza a leer su archivo de entrada (que ahora está vacío). La mayoría de los programas se comportan de esta manera.

Cree un nuevo archivo temporal para la salida, luego muévalo a su lugar.

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

Si su plataforma iconvno tiene -o, puede usar una redirección de shell para el mismo efecto.

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

La spongeutilidad de Colin Watson (incluida en moreutils de Joey Hess ) automatiza esto:

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

Esta respuesta se aplica no solo iconva cualquier programa de filtro, sino también a él. Vale la pena mencionar algunos casos especiales:

  • GNU sed y Perl -ptienen una -iopción para reemplazar archivos en su lugar.
  • Si el archivo es muy grande, el filtro sólo está modificando o eliminando algunas partes, pero nunca añadir cosas (por ejemplo grep, tr, sed 's/long input text/shorter text/'), y te gusta vivir peligrosamente, es posible que desee realmente modificar el archivo en su lugar (las otras soluciones mencionadas aquí crean una nuevo archivo de salida y muévalo a su lugar al final, de modo que los datos originales no cambien si el comando se interrumpe por cualquier motivo).
Gilles 'SO- deja de ser malvado'
fuente
3
No estoy muy seguro de si la autoría de spongedebe atribuirse exclusivamente a Joey Hess; es el paquete moreutilsque incluye lo spongeque mantiene, pero en lo que respecta al origen de sponge, siguiendo los enlaces de la página de inicio de moreutils, he encontrado originalmente publicado y sugerido para su inclusión por Colin Watson: "Joey escribe sobre la falta de nuevas herramientas que encaja en la filosofía de Unix. Mi favorito de las cosas que he escrito es sponge"(Lun, 06 de febrero de 2006).
imz - Ivan Zakharyaschev
3
Uso Mac OS, no hay opción -o en iconv, tengo que cambiar `iconv -f cp1251 -t utf8 -o" $ file.new "" $ file "` aiconv -f cp1251 -t utf8 "$file" > "$file.new"
code4j
Algunos comandos, como sort, son bastante inteligentes con respecto a los -oparámetros, y si detectan que el archivo de salida es igual a la entrada, administran internamente un archivo temporal, por lo que simplemente funciona.
jesjimher
56

Una alternativa es recode, que utiliza la biblioteca libiconv para algunas conversiones. Su comportamiento es reemplazar el archivo de entrada con la salida, por lo que esto funcionará:

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

Como recodeacepta múltiples archivos de entrada como parámetro, puede ahorrar el forbucle:

recode cp1251..utf8 *.php
hombre trabajando
fuente
2
Gracias, esto merece más votos a favor. Solo me pregunto dónde se mira en el manual sobre los 2 puntos entre las codificaciones ...
neurino
2
"SOLICITUD a menudo se ve ANTES ... DESPUÉS, ANTES y DESPUÉS de ser charsets". Ese manual es realmente difícil de seguir con todos esos puntos dobles (que son parte de la sintaxis) y puntos triples (lo que significa más de esto). Un consejo: intente en su info recodelugar. Es más detallado.
manatwork
4

Por ahora

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

Funciona de maravilla

galeksandrp
fuente
55
Al principio, realmente pensé que funcionaba. Pero parece que la salida que excede los 32K se corta, y con aún más entrada desencadena volcados de núcleo.
x-yuri
1

Puede usar Vim en modo Ex:

ex -sc '%!iconv -f cp1251 -t utf8' -cx "$file"
  1. % seleccione todas las líneas

  2. ! ejecutar comando

  3. x guardar y cerrar

Steven Penny
fuente
0

Aquí hay un ejemplo simple . Debería darle una información suficiente para comenzar.

#!/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
fuente
0
echo "`iconv -f cp1251 -t utf8 $file`" > "$file"

funciona para mi

CONSTANTE
fuente
0

Puedes usar find, al menos esto funcionó para mí en Raspbian Stretch:

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

Una opción es usar perlla interfaz iconvy su -imodo para la edición in situ:

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

Con GNU awk, también puedes hacer algo como:

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

El ksh93shell también tiene un >;operador para aquello que almacena la salida en un archivo temporal que se renombra al archivo redirigido si el comando fue exitoso:

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