像在 MSYS2 中一樣在 Linux 中使用 CRLF(輸入)處理 Bash 腳本?
假設我有以下簡單的腳本
tmp.sh
:echo "testing" stat . echo "testing again"
儘管它是微不足道的,但它具有
\r\n
(即 CRLF,即輸入+換行)作為行尾。由於網頁不會保留行尾,這裡是一個 hexdump:$ hexdump -C tmp.sh 00000000 65 63 68 6f 20 22 74 65 73 74 69 6e 67 22 0d 0a |echo "testing"..| 00000010 73 74 61 74 20 2e 0d 0a 65 63 68 6f 20 22 74 65 |stat ...echo "te| 00000020 73 74 69 6e 67 20 61 67 61 69 6e 22 0d 0a |sting again"..| 0000002e
現在,它具有 CRLF 行結尾,因為該腳本是在 Windows 上的 MSYS2 下啟動和開發的。因此,當我在 MSYS2 中的 Windows 10 上執行它時,我得到了預期:
$ bash tmp.sh testing File: . Size: 0 Blocks: 40 IO Block: 65536 directory Device: 8e8b98b6h/2391513270d Inode: 281474976761067 Links: 1 Access: (0755/drwxr-xr-x) Uid: (197609/ USER) Gid: (197121/ None) Access: 2020-04-03 10:42:53.210292000 +0200 Modify: 2020-04-03 10:42:53.210292000 +0200 Change: 2020-04-03 10:42:53.210292000 +0200 Birth: 2019-02-07 13:22:11.496069300 +0100 testing again
但是,如果我將此腳本複製到 Ubuntu 18.04 機器並在那裡執行它,我會得到其他東西:
$ bash tmp.sh testing stat: cannot stat '.'$'\r': No such file or directory testing again
在其他具有相同行尾的腳本中,我在 Ubuntu bash 中也遇到了這個錯誤:
line 6: $'\r': command not found
…可能來自空行。
因此,很明顯,Ubuntu 中的某些東西會在輸入時阻塞。我見過BASH 和輸入行為:
它與 Bash 沒有任何關係: \r 和 \n 由終端解釋,而不是由 Bash
…但是,我想這僅適用於在命令行上逐字輸入的內容;這裡的
\r
and\n
已經在腳本本身中輸入了,所以它一定是 Bash 解釋了\r
這裡。這是 Ubuntu 中的 Bash 版本:
$ bash --version GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
…這裡是 MSYS2 中的 Bash 版本:
$ bash --version GNU bash, version 4.4.23(2)-release (x86_64-pc-msys)
(它們看起來並沒有那麼大的區別……)
無論如何,我的問題是 - 有沒有辦法說服 Ubuntu/Linux 上的 Bash
\r
忽略一個有效命令的一部分,哪個 bash 解釋為這樣)?編輯:無需轉換腳本本身(所以它保持不變,帶有 CRLF 行結尾,如果以這種方式檢查,比如在 git 中)EDIT2:我更喜歡這種方式,因為與我一起工作的其他人可能會在 Windows 文本編輯器中重新打開腳本,可能會
\r\n
再次將其重新引入腳本並送出;然後我們可能會以無休止的送出流結束,這可能只不過是\r\n
污染\n
儲存庫的轉換。EDIT2:@Kusalananda 在評論中提到
dos2unix
(sudo apt install dos2unix
);請注意,只是寫這個:$ dos2unix tmp.sh dos2unix: converting file tmp.sh to Unix format...
…將就地轉換文件;要將其輸出到標準輸出,必須設置標準輸入重定向:
$ dos2unix <tmp.sh | hexdump -C 00000000 65 63 68 6f 20 22 74 65 73 74 69 6e 67 22 0a 73 |echo "testing".s| 00000010 74 61 74 20 2e 0a 65 63 68 6f 20 22 74 65 73 74 |tat ..echo "test| 00000020 69 6e 67 20 61 67 61 69 6e 22 0a |ing again".| 0000002b
…然後,原則上,可以在 Ubuntu 上執行它,這在這種情況下似乎有效:
$ dos2unix <tmp.sh | bash testing File: . Size: 20480 Blocks: 40 IO Block: 4096 directory Device: 816h/2070d Inode: 1572865 Links: 27 Access: (1777/drwxrwxrwt) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2020-04-03 11:11:00.309160050 +0200 Modify: 2020-04-03 11:10:58.349139481 +0200 Change: 2020-04-03 11:10:58.349139481 +0200 Birth: - testing again
但是,除了要記住的稍微凌亂的命令之外,這也會改變 bash 語義,因為 stdin 不再是終端;這可能適用於這個簡單的範例,但請參閱例如https://stackoverflow.com/questions/23257247/pipe-a-script-into-bash以了解更大的問題。
據我所知,沒有辦法告訴 Bash 接受 Windows 風格的行尾。
在涉及 Windows 的情況下,通常的做法是依靠 Git 在送出時使用
autocrlf
配置標誌自動轉換行尾的能力。例如,請參閱GitHub 關於 line endings 的文件,該文件並非特定於 GitHub。這樣,文件在儲存庫中以 Unix 樣式的行結尾送出,並根據每個客戶端平台進行適當的轉換。(相反的問題不是問題:MSYS2 在 Windows 上與 Unix 風格的行尾一起工作得很好。)