Как я могу сделать так, чтобы iconv заменил входной файл на преобразованный вывод?


70

У меня есть скрипт bash, который перечисляет все файлы * .php в каталоге и применяется iconvк нему. Это получает вывод в STDOUT.

Поскольку при добавлении -oпараметра (по моему опыту) фактически записывается пустой файл, вероятно, до того, как произойдет преобразование, как я могу настроить свой сценарий так, чтобы он выполнял преобразование, а затем перезаписывал входной файл?

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

Ответы:


76

Это не работает, потому что iconvсначала создается выходной файл (поскольку файл уже существует, он усекает его), а затем начинает читать его входной файл (который теперь пуст). Большинство программ ведут себя так.

Создайте новый временный файл для вывода, затем переместите его на место.

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

Если ваша платформа iconvне имеет -o, вы можете использовать перенаправление оболочки для того же эффекта.

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

spongeУтилита Колина Уотсона (включена в moreutils Джои Хесса ) автоматизирует это:

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

Этот ответ относится не только iconvк любой программе фильтрации. Стоит упомянуть несколько особых случаев:

  • GNU sed и Perl -pимеют -iвозможность заменить файлы на месте.
  • Если файл очень большой, ваш фильтр не только изменение или удаление некоторых частей , но никогда не добавлять вещи (например grep, tr, sed 's/long input text/shorter text/'), и вам нравится жить опасно, вы можете действительно изменить файл в месте (другие решения , упомянутые здесь , создают новый выходной файл и переместите его на место в конце, чтобы исходные данные не изменялись, если по какой-либо причине команда была прервана).

3
Я не совсем уверен, spongeдолжно ли авторство принадлежать исключительно Джои Хесс; это пакет, moreutilsкоторый включает в себя, spongeчто он поддерживает, но что касается происхождения sponge, то, следуя ссылкам с домашней страницы moreutils, я обнаружил, что он изначально размещен и предложен для включения Колином Уотсоном: «Джои пишет об отсутствии новых инструментов, которые вписывается в философию Unix. Мои любимые вещи, которые я написал, sponge"(понедельник, 6 февраля 2006 г.).
imz - Иван Захарящев

3
Я использую Mac OS, в iconv нет опции -o, мне нужно изменить `iconv -f cp1251 -t utf8 -o" $ file.new "" $ file "` наiconv -f cp1251 -t utf8 "$file" > "$file.new"
code4j

Некоторые команды, например sort, довольно умны в отношении -oпараметров, и если они обнаруживают, что выходной файл совпадает с вводом, они внутренне управляют временным файлом, поэтому он просто работает.
18:18.

56

Альтернатива recode, которая использует библиотеку libiconv для некоторых преобразований. Его поведение заключается в замене входного файла с выходным, так что это будет работать:

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

Поскольку recodeпринимает несколько входных файлов в качестве параметра, вы можете сэкономить forцикл:

recode cp1251..utf8 *.php

2
Спасибо, это заслуживает большего количества голосов. Просто интересно, где в руководстве
смотрят

2
«REQUEST часто выглядит как BEFORE..AFTER, причем BEFORE и AFTER являются кодировками». В этом руководстве действительно трудно следовать со всеми этими двойными точками (которые являются частью синтаксиса) и тройными точками (которые означают больше этого). Совет: попробуйте info recodeвместо этого. Более многословно.
Манатворк

4

Сейчас

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

работает как шарм


5
Сначала я действительно думал, что это работает. Но кажется, что выходной сигнал, превышающий 32 КБ, обрезан, и с еще большим вводом он вызывает дамп ядра.
x-yuri

1

Вы можете использовать Vim в режиме Ex:

ex -sc '%!iconv -f cp1251 -t utf8' -cx "$file"
  1. % выбрать все строки

  2. ! Команда Run

  3. x сохранить и закрыть


0

Вот простой пример . Это должно дать вам достаточно информации, чтобы начать.

#!/bin/bash
#conversor.sh
#Author.....: dede.exe
#E-mail.....: dede.exe@gmail.com
#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;


0

Вы можете использовать find, по крайней мере, это сработало для меня на Raspbian Stretch:

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

0

Одним из вариантов является использование perlинтерфейса iconvи его -iрежима для редактирования на месте:

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

С GNU awkвы также можете сделать что-то вроде:

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

В ksh93оболочке также есть >;оператор для того, что сохраняет выходные данные во временном файле, который переименовывается в перенаправленный файл, если команда была успешной:

for f in *.php; do
  iconv -f cp1252 -t utf-8 < $f >; $f
done
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.