下面的脚本当前删除了 ^M 字符 ( Ctrl+V+M
)。我觉得有点啰嗦,但我还需要添加 ^I 和我将来可能看到的任何其他字符。
有没有更简单的方法来添加 ^I ( Ctrl+V+I
)?这是我大约 6 个月前参加为期 2 天的 shell 编程课程后为自己编写的第一个脚本。我不确定我是否让它比需要的时间更长,所以任何一般性的提示也将不胜感激。
#!/bin/bash
echo "$# item(s) to review."
question='Do you want to remove the ^M characters?'
for file
do
if grep "^M" "$file" >> /dev/null 2> /dev/null
then
echo "$file contains special characters"
echo $question
read answer
if [[ "$answer" == [yY] ]]
then
cat "$file" | sed "s/^M//" > "$file.safe"
echo "Special characters have been removed and $file.safe has been created."
elif [[ "$answer" == [yY][eE][sSaA]* ]]
then
cat "$file" | sed "s/^M//" > "$file.safe"
echo "Special characters have been removed and $file.safe has been created."
else
echo "Special characters have NOT been removed."
fi
elif [[ -d $file ]]
then
echo "$file is a directory"
else
echo "No special characters in $file"
fi
done
答案1
这肯定比需要的时间要长得多。您所需要的只是tr
公用事业,加上一个循环和重定向来作用于作为参数传递给脚本的文件。
#!/bin/sh
for file do
tr -d '\r\t' <"$file" >"$file.safe"
done
使用选项-d
,tr
删除指定的字符。要删除的字符作为第一个非选项参数一起传递。您可以使用反斜杠转义来表示特殊字符:\n
换行符 (^J)、\r
回车符 (^M)、\t
制表符 (^I) 等。
我没有复制询问用户的代码,因为它毫无意义。无论如何,目录都会导致重定向错误,并且调用者的工作实际上是不请求无意义的操作,例如将目录视为常规文件,因此我也跳过了该部分。
如果要替换原始文件,请写入临时文件,然后将结果移动到位。
#!/bin/sh
for file do
tmp="$(TMPDIR=$(dirname -- "$file") mktemp)"
tr -d '\r\t' <"$file" >"$tmp" && mv -f -- "$tmp" "$file"
done
临时文件名的构造是mktemp
为了使脚本更加健壮。只要您对包含该文件的目录具有写入权限,它就可以工作,而不会存在覆盖现有文件的风险。即使该目录可由可能尝试注入其他数据的其他用户写入(参考资料中的潜在问题/tmp
),它也是安全的。
mv
仅当调用成功时才会调用该命令,因此如果失败(例如磁盘已满),tr
则不存在丢失数据的风险。tr
如果您想避免用不包含任何特殊字符的新的相同文件替换该文件,有两种方法:
您可以先检查特殊字符。有几种方法可以做到这一点。一种方法是删除除特殊字符之外的所有内容并计算结果字符的数量。作为一种优化,通过管道传输,
head -c 1
这样如果在顶部附近发现特殊字符,则无需遍历整个文件:这样,如果没有什么可做的,则计数为 0,否则计数为 1。if [ "$(tr -dc '\r\t' <"$file" | head -c 1 | wc -c)" -ne 0 ]; then tr -d '\r\t' <"$file" >"$tmp" && mv -f -- "$tmp" "$file" fi
您可以进行转换,然后检查它是否与原始版本相同。如果文件通常已经处于所需状态,则速度可能会较慢。另一方面,此技术适用于不容易确定文件是否处于所需状态的情况。
tr -d '\r\t' <"$file" >"$tmp" && if cmp -s "$tmp" "$file"; then rm -- "$tmp" else mv -f -- "$tmp" "$file" fi
答案2
您可以在脚本周围放置一个循环。所以:
for c in "^I" "^M"; do
for file; do
if grep "$c" "$file"; then
...
etc.
...
fi
done
done
答案3
我更喜欢这个 Perl One 衬垫。 '\cM' 是控制 M 字符。原始文件将以扩展名“.bak”进行备份。此扩展名可以是您的选择。
perl -i.bak -pe 's/\cM//g;' file(s)
使用要删除的一类字符的示例。在括号中,perl 会找到 control-I 和 control-M 并将其删除。不过我还没有具体测试过。
perl -i.bak -pe 's/[\cM\cI]//g;' files(s)
答案4
你有没有想过使用
tr -d .....<characterlist>....
例如,删除所有不可打印的字符并放入另一个文件中:
cat filename | tr -cd '[:print:]' >/tmp/x.out
修改字符列表以适合您的应用程序...请参阅tr
手册页以获取更多信息。
这也很好,因为允许正则表达式范围:
echo '\001\002\003\004' | tr -d '[\001-\003]' | od -c