如何從文件中刪除多個特殊字元?

如何從文件中刪除多個特殊字元?

下面的腳本目前刪除了 ^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

相關內容