You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

脚本中iconv命令异常:部分文件变0字节且生成.bak文件

问题:批量转换非UTF-8文件时出现0字节文件及.bak后缀

原问题描述

我正在编写一个脚本,用于检测目录中包含非UTF-8字符的文件,若存在则获取该文件的编码类型并执行iconv转换操作。脚本代码如下:

find  <directory> |sed '1d'>/<directory>/filelist.txt

while read filename
do
file_nm=${filename%%.*}
ext=${filename#*.}
echo $filename
q=`grep -axv '.*' $filename|wc -l`
echo $q
r=`file -i $filename|cut -d '=' -f 2`
echo $r
#file_repair=$file_nm
if [ $q -gt 0 ]; then
iconv -f $r -t utf-8 -c ${file_nm}.${ext} >${file_nm}_repaired.${ext}

mv ${file_nm}_repaired.${ext} ${file_nm}.${ext}

fi
done< <directory>/filelist.txt

运行脚本后,多个文件变为0字节,且文件名被追加.bak后缀,执行ls| grep 'bak' | wc -l结果为36。请问哪里出错了?

错误原因分析

  • 文件名处理逻辑错误:

    • ${filename%%.*}会从右往左删除最后一个.及之后的所有内容,若文件名包含多个.(如doc.v2.txt),会错误截断文件名;若文件无后缀(如README),${filename#*.}会返回完整路径,拼接后得到README.README这种不存在的文件名,导致iconv读取失败,输出空文件,最终覆盖原文件造成0字节。
    • 未处理路径中的空格或特殊字符,read filename会拆分带空格的文件名,导致后续操作错误。
  • UTF-8检测不可靠:

    • grep -axv '.*' $filename|wc -l逻辑存在问题:-x要求整行匹配,.*匹配任意UTF-8字符,反向匹配后统计的是无法被UTF-8解析的行数,但该方法误判率高(比如合法非UTF-8文本可能漏检,二进制文件会被误判),还会在处理目录时返回非零行数,触发错误转换。
  • 编码检测与转换问题:

    • file -i返回的编码可能不被iconv兼容(比如返回unknown-8bit),此时iconv会报错,输出空文件,覆盖原文件后变成0字节。
    • 直接用mv覆盖原文件,无备份机制,一旦转换失败就会丢失原文件内容;你看到的.bak后缀可能是系统或其他工具在文件被意外修改时自动生成的备份。
  • 未过滤目录:

    • find <directory>会列出所有子目录和文件,脚本尝试处理目录时,grep和iconv会报错,生成空的_repaired文件,后续mv操作可能破坏目录结构或生成无效文件。

修正后的脚本

#!/bin/bash
TARGET_DIR="<directory>"

# 只查找普通文件,跳过目录,处理带空格的文件名
find "$TARGET_DIR" -type f -print0 | while IFS= read -r -d '' filename
do
    # 检测文件是否为非UTF-8编码
    if ! iconv -f utf-8 -t utf-8 -c "$filename" >/dev/null 2>&1; then
        # 获取文件编码,过滤出iconv兼容的编码名
        encoding=$(file -i "$filename" | sed -n 's/.*charset=\([^;]*\).*/\1/p')
        # 处理file返回的unknown-8bit,默认用latin1
        [[ "$encoding" == "unknown-8bit" ]] && encoding="latin1"

        # 生成备份文件名
        backup_file="${filename}.bak"
        # 先备份原文件
        cp "$filename" "$backup_file"

        # 执行编码转换
        if iconv -f "$encoding" -t utf-8 -c "$backup_file" > "$filename"; then
            echo "成功转换:$filename(原文件备份为$backup_file)"
        else
            # 转换失败,恢复原文件
            cp "$backup_file" "$filename"
            echo "转换失败,已恢复原文件:$filename"
            rm "$backup_file"
        fi
    fi
done

修正说明

  1. 使用find -type f -print0和read -r -d ''处理带空格或特殊字符的文件名,避免拆分错误。
  2. 用iconv -f utf-8 -t utf-8 -c直接检测文件是否为UTF-8,更可靠。
  3. 对file返回的编码做兼容处理,默认将unknown-8bit转为latin1,避免iconv报错。
  4. 先备份原文件到.bak,转换成功则保留备份,失败则恢复原文件并删除备份,避免数据丢失。
  5. 只处理普通文件,跳过目录,避免无效操作。

内容的提问来源于stack exchange,提问作者noob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 05:35:19