Linux

像在 MSYS2 中一樣在 Linux 中使用 CRLF(輸入)處理 Bash 腳本?

  • June 18, 2021

假設我有以下簡單的腳本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

…但是,我想這僅適用於在命令行上逐字輸入的內容;這裡的\rand\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 在評論中提到dos2unixsudo 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 風格的行尾一起工作得很好。)

引用自:https://unix.stackexchange.com/questions/577663