本文目录导读:

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
建议
- 先备份:修改前确保有文件备份
- 先预览:使用
-n或--dry-run参数预览修改 - 测试小范围:先在小目录测试
- 注意编码:确保文件编码(UTF-8, GBK等)与换行符兼容
选择哪种方法取决于你的系统环境和需求,Python 脚本最灵活,适用于所有平台。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播