使用Bash脚本转换文件内容大小写时文件内容被清空的问题
解决Bash脚本转换大小写时清空文件的问题
嘿,我太懂这种崩溃的感觉了!你大概率是踩了Bash脚本里一个超级常见的坑——直接用重定向覆盖原文件导致的内容清空。
问题根源
当你写出类似这样的代码时:
tr 'a-z' 'A-Z' $file > $file
Bash的执行顺序是先处理重定向操作:它会先打开$file并清空里面的内容,然后才会去读取这个已经空了的文件,传给tr命令处理。最后tr输出空内容,再写入已经被清空的文件,结果自然就是所有文件都变成空的了。
正确的解决方案
下面给你几个可靠的解决方法,按通用性排序:
1. 用临时文件中转(最稳妥,跨平台)
这是最安全的做法,通过临时文件先保存转换后的内容,再替换原文件:
#!/bin/bash # 替换成你的目标目录 target_dir="/path/to/your/target/dir" # 遍历目录下的所有条目 for file in "$target_dir"/*; do # 跳过目录,只处理普通文件 [ -f "$file" ] || continue # 创建一个安全的临时文件 temp_file=$(mktemp) # 将转换后的内容写入临时文件 tr 'a-z' 'A-Z' < "$file" > "$temp_file" # 用临时文件覆盖原文件 mv "$temp_file" "$file" done
mktemp会自动生成一个唯一的临时文件名,避免多个脚本同时运行时的冲突,非常稳妥。
2. 使用sed的原地编辑功能
GNU sed(Linux默认)支持-i选项实现原地修改文件,BSD sed(比如macOS上的)需要加一个空后缀-i '':
#!/bin/bash target_dir="/path/to/your/target/dir" for file in "$target_dir"/*; do [ -f "$file" ] || continue # GNU sed 版本(Linux) sed -i 's/[a-z]/\U&/g' "$file" # 如果是 macOS/BSD 系统,替换成下面这句 # sed -i '' 's/[a-z]/\U&/g' "$file" done
这里的\U&是sed的语法:\U表示把后续字符转成大写,&代表匹配到的原字符,g表示全局替换所有小写字符。
3. 使用sponge工具(更简洁)
sponge是moreutils工具包中的工具,它会先把所有输入内容读入内存,再写入目标文件,完美避免了重定向顺序的问题。如果你的系统没有这个工具,可以用包管理器安装(比如apt install moreutils或brew install moreutils):
#!/bin/bash target_dir="/path/to/your/target/dir" for file in "$target_dir"/*; do [ -f "$file" ] || continue tr 'a-z' 'A-Z' < "$file" | sponge "$file" done
额外注意事项
- 如果需要处理隐藏文件(以
.开头的文件),$target_dir/*不会匹配到,你可以改成for file in "$target_dir"/.* "$target_dir"/*,然后再过滤掉.和..这两个特殊目录。 - 所有变量都要用双引号包裹,避免文件名含空格、特殊字符时出现错误。
- 测试脚本时,建议先拿几个备份文件试试,避免意外丢失数据!
内容的提问来源于stack exchange,提问作者JesusR
相关产品推荐
相关产品推荐

