批量修改文件末尾换行符脚本

wen 实用脚本 1

本文目录导读:

批量修改文件末尾换行符脚本

  1. Python 脚本(最灵活)
  2. 使用 sed 命令(Linux/Mac)
  3. 使用 dos2unix/unix2dos 工具
  4. 高级 Python 脚本(带更多功能)
  5. 建议

Python 脚本(最灵活)

#!/usr/bin/env python3
"""
批量修改文件末尾换行符
支持: LF (Unix), CRLF (Windows), CR (Old Mac)
"""
import os
import sys
import argparse
from pathlib import Path
def fix_line_endings(file_path, target_ending='LF'):
    """修改单个文件的换行符"""
    # 读取文件内容(二进制模式以确保准确读取)
    with open(file_path, 'rb') as f:
        content = f.read()
    # 检测当前换行符
    if b'\r\n' in content:
        current = 'CRLF'
        has_crlf = True
    elif b'\r' in content and b'\n' not in content:
        current = 'CR'
        has_cr = True
    else:
        current = 'LF'
        has_lf = True
    # 转换为目标换行符
    if target_ending == 'LF':
        # 先将 CRLF 和 CR 都转为 LF
        content = content.replace(b'\r\n', b'\n')
        content = content.replace(b'\r', b'\n')
        new_ending = b'\n'
    elif target_ending == 'CRLF':
        # 先将 CR 转为 LF,再将 LF 转为 CRLF
        content = content.replace(b'\r\n', b'\n')
        content = content.replace(b'\r', b'\n')
        content = content.replace(b'\n', b'\r\n')
        new_ending = b'\r\n'
    elif target_ending == 'CR':
        # 先将 CRLF 转为 LF,再将 LF 转为 CR
        content = content.replace(b'\r\n', b'\n')
        content = content.replace(b'\n', b'\r')
        new_ending = b'\r'
    else:
        print(f"未知的目标换行符: {target_ending}")
        return False
    # 写回文件
    with open(file_path, 'wb') as f:
        f.write(content)
    return True
def process_files(directory, pattern='*', target='LF', recursive=True):
    """批量处理文件"""
    directory = Path(directory)
    if not directory.exists():
        print(f"目录不存在: {directory}")
        return
    # 收集文件
    if recursive:
        files = list(directory.rglob(pattern))
    else:
        files = list(directory.glob(pattern))
    if not files:
        print(f"在 {directory} 中未找到匹配 {pattern} 的文件")
        return
    print(f"找到 {len(files)} 个文件")
    success_count = 0
    for file_path in files:
        if file_path.is_file():
            try:
                if fix_line_endings(file_path, target):
                    success_count += 1
                    print(f"✓ {file_path.name}")
            except Exception as e:
                print(f"✗ {file_path.name}: {e}")
    print(f"\n处理完成: {success_count}/{len(files)} 个文件已修改")
def main():
    parser = argparse.ArgumentParser(description='批量修改文件末尾换行符')
    parser.add_argument('directory', help='目标目录')
    parser.add_argument('-p', '--pattern', default='*', help='文件匹配模式(如 *.txt, *.py)')
    parser.add_argument('-t', '--target', choices=['LF', 'CRLF', 'CR'], default='LF',
                       help='目标换行符类型 (默认: LF)')
    parser.add_argument('--no-recursive', action='store_true', 
                       help='不递归子目录')
    args = parser.parse_args()
    process_files(
        args.directory,
        pattern=args.pattern,
        target=args.target,
        recursive=not args.no_recursive
    )
if __name__ == '__main__':
    main()

使用方法:

# 将当前目录所有文件转为 LF
python fix_lineendings.py . -t LF
# 将所有 .txt 文件转为 CRLF
python fix_lineendings.py . -p "*.txt" -t CRLF
# 递归处理子目录中的所有 .py 文件
python fix_lineendings.py . -p "*.py" -t LF

使用 sed 命令(Linux/Mac)

#!/bin/bash
# batch_fix_lineendings.sh
# 将目录下所有文件转换为 LF (Unix)
find . -type f -name "*.txt" -exec sed -i 's/\r$//' {} \;
# 将目录下所有文件转换为 CRLF (Windows)
find . -type f -name "*.txt" -exec sed -i 's/$/\r/' {} \;
# 更安全的方式:创建备份
find . -type f -name "*.txt" -exec sed -i.bak 's/\r$//' {} \;

使用 dos2unix/unix2dos 工具

#!/bin/bash
# batch_fix_lineendings_tool.sh
# 首先安装工具(如果还没有)
# Ubuntu/Debian: sudo apt-get install dos2unix
# CentOS/RHEL: sudo yum install dos2unix
# Mac: brew install dos2unix
# 批量转换所有 .txt 文件为 Unix 格式
find . -name "*.txt" -type f -exec dos2unix {} \;
# 批量转换为 Windows 格式
find . -name "*.txt" -type f -exec unix2dos {} \;
# 只检查不修改(查看哪些文件需要转换)
find . -name "*.txt" -type f -exec dos2unix -ic {} \;

高级 Python 脚本(带更多功能)

#!/usr/bin/env python3
"""
高级批量换行符修改脚本
功能:备份、预览、过滤二进制文件、统计
"""
import os
import sys
import shutil
from pathlib import Path
class LineEndingFixer:
    def __init__(self, directory, target='LF', backup=False, dry_run=False):
        self.directory = Path(directory)
        self.target = target
        self.backup = backup
        self.dry_run = dry_run
        self.stats = {'processed': 0, 'modified': 0, 'skipped': 0, 'errors': 0}
    def is_text_file(self, file_path):
        """检查是否为文本文件(跳过二进制文件)"""
        try:
            with open(file_path, 'rb') as f:
                chunk = f.read(1024)
                # 检查是否有 null 字节
                return b'\x00' not in chunk
        except:
            return False
    def detect_line_ending(self, content):
        """检测文件当前的换行符类型"""
        if b'\r\n' in content:
            return 'CRLF'
        elif b'\r' in content and b'\n' not in content:
            return 'CR'
        else:
            return 'LF'
    def fix_file(self, file_path):
        """修改单个文件"""
        try:
            with open(file_path, 'rb') as f:
                content = f.read()
            if not content:
                return True
            current = self.detect_line_ending(content)
            if current == self.target:
                return True
            # 创建备份
            if self.backup and not self.dry_run:
                backup_path = file_path.with_suffix(file_path.suffix + '.bak')
                shutil.copy2(file_path, backup_path)
            # 转换
            if self.target == 'LF':
                content = content.replace(b'\r\n', b'\n')
                content = content.replace(b'\r', b'\n')
            elif self.target == 'CRLF':
                content = content.replace(b'\r\n', b'\n')
                content = content.replace(b'\r', b'\n')
                content = content.replace(b'\n', b'\r\n')
            elif self.target == 'CR':
                content = content.replace(b'\r\n', b'\n')
                content = content.replace(b'\n', b'\r')
            if not self.dry_run:
                with open(file_path, 'wb') as f:
                    f.write(content)
            self.stats['modified'] += 1
            return True
        except Exception as e:
            print(f"  ❌ {file_path.name}: {e}")
            self.stats['errors'] += 1
            return False
    def process(self, pattern='*', recursive=True):
        """处理文件"""
        print(f"📁 目录: {self.directory}")
        print(f"🎯 目标: {self.target}")
        print(f"📋 预览模式: {'是' if self.dry_run else '否'}")
        print(f"💾 创建备份: {'是' if self.backup else '否'}")
        print("-" * 50)
        if recursive:
            files = list(self.directory.rglob(pattern))
        else:
            files = list(self.directory.glob(pattern))
        # 过滤文本文件
        files = [f for f in files if f.is_file() and self.is_text_file(f)]
        print(f"📊 找到 {len(files)} 个文本文件")
        for file_path in files:
            self.stats['processed'] += 1
            relative = file_path.relative_to(self.directory)
            print(f"  📄 {relative}", end=' ')
            if self.fix_file(file_path):
                print("✅")
            else:
                print("❌")
        print("-" * 50)
        print(f"📊 统计:")
        print(f"  • 处理文件: {self.stats['processed']}")
        print(f"  • 修改文件: {self.stats['modified']}")
        print(f"  • 跳过文件: {self.stats['skipped']}")
        print(f"  • 错误文件: {self.stats['errors']}")
def main():
    import argparse
    parser = argparse.ArgumentParser(description='批量修改文件换行符')
    parser.add_argument('directory', nargs='?', default='.', help='目标目录')
    parser.add_argument('-p', '--pattern', default='*', help='文件匹配模式')
    parser.add_argument('-t', '--target', choices=['LF', 'CRLF', 'CR'], 
                       default='LF', help='目标换行符')
    parser.add_argument('-b', '--backup', action='store_true', help='创建备份')
    parser.add_argument('-n', '--dry-run', action='store_true', help='预览模式')
    parser.add_argument('--no-recursive', action='store_true', help='不递归')
    args = parser.parse_args()
    fixer = LineEndingFixer(
        args.directory,
        target=args.target,
        backup=args.backup,
        dry_run=args.dry_run
    )
    fixer.process(
        pattern=args.pattern,
        recursive=not args.no_recursive
    )
if __name__ == '__main__':
    main()

高级用法:

# 预览要修改的文件(不实际修改)
python advanced_fixer.py . -p "*.txt" -n
# 修改并创建备份
python advanced_fixer.py . -t CRLF -b
# 修改特定类型的文件
python advanced_fixer.py . -p "*.py" -t LF

建议

  1. 先备份:修改前确保有文件备份
  2. 先预览:使用 -n--dry-run 参数预览修改
  3. 测试小范围:先在小目录测试
  4. 注意编码:确保文件编码(UTF-8, GBK等)与换行符兼容

选择哪种方法取决于你的系统环境和需求,Python 脚本最灵活,适用于所有平台。

抱歉,评论功能暂时关闭!