如何从文件中删除多个特殊字符?

如何从文件中删除多个特殊字符?

下面的脚本当前删除了 ^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

使用选项-dtr删除指定的字符。要删除的字符作为第一个非选项参数一起传递。您可以使用反斜杠转义来表示特殊字符:\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

相关内容