Shell
read
命令在 Makefile 中不起作用
我有一個 make 腳本來執行 3 個任務:
- 導入 MySQL 數據庫
- 移動配置文件
- 配置配置文件
對於這些任務,腳本需要 3 個輸入:
- MySQL主機
- MySQL 使用者名
- MySQL 密碼
出於某種原因,每當我
read
輸入時,它都會保存到正確的變數中,但內容是我保存的變數名,沒有第一個字母。我似乎無法找出它為什麼這樣做。這是
Makefile
:SHELL := /bin/bash default: @echo "Welcome!";\ echo -n "Please enter the MySQL host (default: localhost):";\ read host;\ host=${host:-localhost};\ echo -n "Please enter the MySQL username:";\ read username;\ echo -n "Please enter the MySQL password:";\ read -s password;\ mv includes/config.php.example includes/config.php 2>/dev/null;true;\ sed 's/"USER", ""/"USER", "$(username)"/g' includes/config.php > includes/config.php;\ sed 's/"PASSWORD", ""/"PASSWORD", "$(password)"/g' includes/config.php > includes/conf$ echo $username;\ echo $password;\ mysql -u "$username" -p"$password" codeday-team < ./codeday-team.sql;\ echo "Configuration complete. For further configuration options, check the config file$ exit 0;
輸出是:
Welcome! Please enter the MySQL host (default: localhost): Please enter the MySQL username:<snip> Please enter the MySQL password:sername assword ERROR 1045 (28000): Access denied for user 'sername'@'localhost' (using password: YES) Configuration complete. For further configuration options, check the config file includes/config.php
如您所見,它輸出
sername
和assword
。對於我的生活,我無法解決這個問題!雖然這篇文章的目的是解決
read
錯誤,但我會很感激任何建議、錯誤報告或建議。我可以獎勵他們。謝謝你的幫忙!
你
make
在那裡混合shell和變數。兩者make
和 shell 都$
用於它們的變數。在 Makefile 中,變數是
$(var)
or$v
用於單字母變數,$var
或者${var}
在 shell 中。但是如果你寫
$var
在一個 Makefile 中,make
就會把它理解為$(v)ar
. 如果您想將文字傳遞$
給 shell,您需要將其輸入為$$
,如 in$$var
or$${var}
這樣它就成為shell 的$var
or${var}
。此外,
make
執行sh
,而不是bash
解釋該程式碼(編輯,抱歉錯過了您的上述程式碼,請注意,許多系統根本SHELL := /bin/bash
沒有bash
,並且是 GNU 特定的),因此您需要在那裡使用語法。,是/語法,不是語法。/bin``bash``:=``sh``echo -n``read -s``zsh``bash``sh
最好在這裡添加一個
zsh
/bash
腳本來做到這一點(並添加一個建構依賴項bash
)。就像是:#! /usr/bin/env bash printf 'Welcome\nPlease enter the MySQL host (default: localhost): ' read host || exit host=${host:-localhost} printf "Please enter the MySQL username: " read username || exit printf "Please enter the MySQL password:" IFS= read -rs password || exit printf '\n' mv includes/config.php.example includes/config.php 2>/dev/null repl=${password//\\/\\\\} repl=${repl//&/\\&} repl=${repl//:/\\:} { rm includes/config.php && sed 's/"USER", ""/"USER", "'"$username"'"/g s:"PASSWORD", "":"PASSWORD", "'"$repl"'"/g' > includes/config.php } < includes/config.php || exit mysql -u "$username" -p"$password" codeday-team < ./codeday-team.sql || exit echo "Configuration complete. For further configuration options, check the config file"
它解決了更多問題:
-r
如果要允許使用者在密碼中使用反斜杠,則在讀取密碼時需要。- 如果
IFS=
您想允許使用者擁有以空格開頭或結尾的密碼,則需要- 同樣適用於使用者名,但在這裡我們假設使用者不會對使用者名使用任何愚蠢的東西,並且可以將空白剝離和反斜杠處理視為 /feature/。
- 你的
;true
後繼mv
沒有做你認為它做的事。它不會取消errexit
選項的效果(對於make
使用 呼叫 shell 的實現-e
)。你需要||true
。在這裡,我們沒有使用,errexit
而是|| exit
在需要的地方自行處理錯誤。- 您需要轉義反斜杠
&
和s:pattern:repl:
分隔符(此處:
),否則它將不起作用(並且可能會產生令人討厭的副作用)。- 你不能這樣做
sed ... < file > file
,因為在開始file
之前會被截斷sed
。一些sed
實現支持一個-i
or-i ''
選項。或者,您可以使用perl -pi
. 在這裡,我們正在做相當於perl -pi
手動操作(在打開以供讀取後刪除並重新創建輸入文件),但不處理文件的元數據。在這裡,最好使用範例一作為輸入,最後一個作為輸出。
它仍然沒有解決更多問題:
- new
config.php
是使用從 current 派生的權限創建的,umask
這可能是世界可讀的並由執行的使用者擁有make
。如果目錄不受其他限制,您可能需要調整umask
和/或更改所有權,includes
因為該文件包含敏感資訊。- 如果密碼包含
"
字元,則可能會中斷(這次是php
)。php
您可能希望禁止這些(通過返回錯誤)或以該文件的正確語法為它們添加另一層轉義。您可能會遇到與反斜杠類似的問題,並且您可能還想排除非 ascii 或控製字元。- 在命令行上傳遞密碼
mysql
通常是個壞主意,因為這意味著它會顯示在ps -f
. 最好使用:mysql --defaults-file=<( printf '[client]\nuser=%s\npassword="%s"\n' "$username" "$password") ...
printf
是內置的,它不會出現在ps
輸出中。
$host
未使用該變數。- 像這樣提示使用者意味著您的腳本不能輕易自動化。您可以從參數中獲取輸入,或者(對於密碼更好)使用環境變數。