如何使iconv用转换后的输出替换输入文件?


69

我有一个bash脚本,该脚本枚举目录中的每个* .php文件并将其应用于iconv它。这将在STDOUT中输出。

由于添加-o参数(根据我的经验)实际上可能在转换发生之前写入了一个空白文件,因此,如何调整脚本以进行转换,然后覆盖输入文件?

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

另请参阅有关“>”的警告
G-Man

Answers:


76

这是行不通的,因为iconv首先创建输出文件(因为该文件已经存在,它将被截断),然后开始读取其输入文件(现在为空)。大多数程序都以这种方式运行。

为输出创建一个新的临时文件,然后将其移动到位。

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

如果您的平台iconv没有-o,则可以使用Shell重定向达到相同的效果。

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

Colin Watson的sponge实用程序(包括在Joey Hess的moreutils中)使此操作自动化:

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

这个答案不仅适用iconv于任何过滤程序,还适用于任何过滤程序。值得一提的一些特殊情况:

  • GNU sed和Perl -p可以-i选择替换文件。
  • 如果您的文件非常大,您的过滤器只修改或删除某些部分,但从来没有增加的东西(例如greptrsed 's/long input text/shorter text/'),你喜欢住危险的是,你可能要真正修改文件的地方(这里提到的其他解决方案创建新的输出文件并将其移到末尾,因此,如果由于任何原因中断了该命令,原始数据将保持不变。

3
我不确定的著作权是否sponge应仅归因于Joey Hess。它的包装moreutils,包括sponge他坚持,但至于起源sponge,通过跟踪的网页的链接moreutils,我发现它最初发布,并建议纳入科林·沃森:“乔伊写关于缺乏新的工具,符合Unix的哲学。我写的这类文章中我最喜欢的是sponge“(2006年2月6日,星期一)。
imz –伊万·扎哈拉里舍夫(Ivan Zakharyaschev)2011年

3
我使用Mac OS,iconv中没有-o选项,我必须将`iconv -f cp1251 -t utf8 -o“ $ file.new”“ $ file”`更改为iconv -f cp1251 -t utf8 "$file" > "$file.new"
code4j 2014年

某些命令(例如sort)在-o参数方面非常聪明,如果它们检测到输出文件与输入相同,则它们在内部管理一个临时文件,因此它可以正常工作。
jesjimher

56

一个替代方法是recode,它使用libiconv库进行某些转换。它的行为是用输出替换输入文件,因此可以正常工作:

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

由于recode接受多个输入文件作为参数,因此可以节省for循环:

recode cp1251..utf8 *.php

2
谢谢,这值得更多的投票。只是想知道手册中哪里盯着编码之间的两个点...
neurino 2012年

2
“ REQUEST通常看起来像BEFORE..AFTER,而BEFORE和AFTER是字符集。”该手册确实很难遵循所有那些双点(语法的一部分)和三点(意味着更多)。一个建议:试试吧info recode。更冗长。
manatwork 2012年

4

目前

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

奇迹般有效


5
起初,我确实认为它可行。但是看来,超过32K的输出已被切断,而如果输入更多,则会触发核心转储。
x-yuri

1

您可以在Ex模式下使用Vim:

ex -sc '%!iconv -f cp1251 -t utf8' -cx "$file"
  1. % 选择所有行

  2. ! 运行命令

  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
echo "`iconv -f cp1251 -t utf8 $file`" > "$file"

为我工作


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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.