我正在尝试编写一个 shell 脚本,它递归地遍历一个目录,然后在每个文件中将所有 大写字母转换为小写字母。需要明确的是,我不是要更改文件名,而是要更改文件中的文本。
注意事项:
- 这是一个旧的 Fortran 项目,我正在努力使其更易于访问
- 我不想创建一个新文件,而是用更改覆盖旧文件
- 此目录中有几个不同的文件扩展名,包括 .par .f .txt 等
解决此问题的最佳方法是什么?
我正在尝试编写一个 shell 脚本,它递归地遍历一个目录,然后在每个文件中将所有 大写字母转换为小写字母。需要明确的是,我不是要更改文件名,而是要更改文件中的文本。
注意事项:
解决此问题的最佳方法是什么?
要将文件从小写转换为大写,您可以使用ex
(标准编辑器的好朋友ed
):
ex -s file <<EOF
%s/[[:upper:]]\+/\L&/g
wq
EOF
或者,如果您喜欢一行内容:
ex -s file <<< $'%s/[[:upper:]]\+/\L&/g\nwq'
结合find
,您可以执行以下操作:
find . -type f -exec bash -c "ex -s -- \"\$0\" <<< $'%s/[[:upper:]]\+/\L&/g\nwq'" {} \;
这种方法对于文件名中的空格和有趣符号是 100% 安全的。不创建、复制或移动辅助文件;文件只被编辑。
使用glenn jackmann的建议,您还可以编写:
find . -type f -exec bash -c 'printf "%s\n" "%s/[[:upper:]]\+/\L&/g" "wq" | ex -- -s "$0"' {} \;
(优点是它避免了尴尬的逃跑;缺点是它更长)。
tr
您可以使用命令并指定字符范围将所有大写字符 (A–Z) 转换为小写字符 (a–z) ,如下所示:
$ tr 'A-Z' 'a-z' <be.fore >af.ter
tr
在为大写和小写转换指定这种范围时,还有一些特殊的语法:
$ tr '[:upper:]' '[:lower:]' <be.fore >af.ter
该tr
实用程序复制给定的输入以生成输出,并替换或删除选定的字符。tr 缩写为 translate 或 transliterate。它将两组字符作为参数,并将第一组中出现的字符替换为另一组中的相应元素,即用于翻译字符。
tr "set1" "set2" < input.txt > output.txt
虽然tr
不支持正则表达式,嗯,它确实支持一系列字符。
只需确保两个参数以相同数量的字符结束即可。如果第二个参数较短,则将重复其最后一个字符以匹配第一个参数的长度。如果第一个参数较短,则第二个参数将被截断以匹配第一个参数的长度。
sed -e 's/\(.*\)/\L\1/g' *
或者您可以通过管道将文件从 find
扩展@nullrevolution 的解决方案:
find /path_to_files -type f -exec sed --in-place -e 's/\(.*\)/\L\1/g' '{}' \;
这一行将查找以 /path_to_files 作为基本目录的所有子目录中的所有文件。
警告:这将更改 */path_to_file* 下每个目录中所有文件的大小写,因此请确保在执行此脚本之前要执行此操作。您可以使用以下方法根据文件扩展名限制查找范围:
find /path_to_files -type f -name \*.txt -exec sed --in-place -e 's/\(.*\)/\L\1/g' '{}' \;
您可能还想在修改原始文件之前备份原始文件:
find /path_to_files -type f -name *.txt -exec sed --in-place=-orig -e 's/(.*)/\L\1/g' '{}' \;
这将保留原始文件名,同时制作未修改的副本,并在文件名后附加“_orig”(即file.txt将变为file.txt-orig)。
每件作品的解释:
find /path_to_file
这会将基本目录设置为提供的路径。
-type f
这将仅在目录层次结构中搜索文件。
-exec COMMAND '{}' \;
这将为每个匹配的文件执行一次提供的命令。'{}'
替换为当前文件名。表示命令的\;
结束。
sed --in-place -e 's/\(.*\)/\L\1/g'
这--in-place
将在不备份文件的情况下对文件进行 cnages。正则表达式使用反向引用\1
来引用整行并\L
转换为小写。
可选的
(对于更古老的解决方案。)
find /path_to_files -type f -exec dd if='{}' of='{}'-lc conv=lcase \;
在类 Unix 环境中识别文本文件可能有点棘手。你可以这样做:
set -e -o noclobber
while read f; do
tr 'A-Z' 'a-z' <"$f" >"f.$$"
mv "$f.$$" "$f"
done < <(find "$start_directory" -type f -exec file {} + | cut -d: -f1)
这将在带有嵌入冒号或换行符的文件名上失败,但应该适用于其他文件名,包括带有空格的文件名。