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