為什麼以下指令在 Bash 中不起作用?
# Ensure TEST is unset
export TEST=''
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
由於某種原因它總是傳回一個空白行。
在單獨的行上執行此操作會得到預期的結果:
# Ensure TEST is unset
export TEST=''
echo "Hello world" > test.txt
TEST=$(cat test.txt)
echo "$TEST"
Hello world!
我怎樣才能用一句話來完成這項工作?
我需要這個來解密GPG包含帶有特殊字元的字串的文件,然後我將其傳遞給安西布爾。
ANSIBLE_VAULT_PASSWORD="$( gpg -d ~/gpg_encrypted_vault_password_file 2>/dev/null )" ansible-playbook etc etc etc
出於某種原因,Ansible 開發人員認為將保管庫密碼儲存在純文字檔案中是安全的,而且我不喜歡每次測試遊戲時都輸入(或複製貼上)長安全密碼,因此將GPG 代理與加密文件結合使用是我能想到的唯一安全的解決方法。
答案1
不要與任何其他預先存在的答案相衝突,但我認為逐步分析可能會有所幫助。 (u1686_grawity 的回答看起來很準確,但我認為這可能更清楚...)
# ensure TEST is unset export TEST=''
好消息:這可能會按您的預期工作。 # 將第一行變成註釋,然後下一行使 TEST 變數具有空字串(零位元組)。
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
shell 注意到 &&。它將運行程式碼的第一部分...
echo "Hello world!" > test.txt
然後,它處理 &&。它查看 echo 命令的回傳值,發現它為零。因此,它可以繼續運行第二個命令。第二個命令是:
TEST="$(cat test.txt)" echo "$TEST"
因此,shell 在這裡要做的第一件事就是替換變數。 “命令替換”在這裡就像一個變量,因此“cat text.txt”命令運行。從 shell 的角度來看,該命令現在看起來更像:
TEST="Hello world!" echo "$TEST"
現在,到目前為止,一切看起來都不錯,但這是一個大問題。 shell 尚未開始執行「TEST="Hello world!"
」指令。相反,shell 注意到仍然存在對 的引用$TEST
,因此 shell 會取代它。
現在,shell 計劃運行的命令如下所示:
TEST="Hello world!" echo ""
我想,如果我要詳細說明的話,我會指出 shell 確實執行了將該命令分解為多個部分的操作。此操作會刪除引號。所以各個部分看起來像這樣:
- 測試
- =
- 你好世界!
- 迴音
- (空值)
好的,現在 shell 終於完成了替換,因此它可以繼續下一個任務,即尋找要運行的可執行程式碼。
A=B C
它注意到命令列以(A
表示變數名,等號指定要為變數賦值,B
表示要賦值的值,然後C
是可選部分,表示要執行的指令的形式開頭)
當 shell 注意到命令列符合這種一般模式時,它將執行的可執行程式碼是處理等號的程式碼。基本上,等號類似於可執行檔名,從某種意義上說,等號最終控制 shell 將執行的程式碼。 shell 繼續執行程式碼來指派名為 的變量,TEST
其值為Hello world!
。
然後,完成後,shell 將啟動請求的命令,該命令使用上面標識的第四部分和第五部分(其中第四部分是命令echo
,第五部分是空字串)。
我回答的原因(呃......我的意思是,sqrt-1 的答案)的工作原理是第二個 && 導致 shell 運行TEST="$(cat test.txt)"
代碼的返回值是,注意它為零,然後才繼續處理第二個之後的內容&&
(因此該echo "$TEST"
部分將$TEST
處理變量,現在一切都已完成)作品)。
請注意,sqrt-1 提供的答案顯示了一些有點浪費的代碼,儘管這是可以理解的,因為它確實直接回答了所提出的問題。它的基本作用是:
首先,將命令分為三個部分:
第三部分僅在第二部分(運行並)成功時運行,而第二部分僅在第一個命令成功時運行,因為有兩個&&
運算符。
另一種變化可能是:
echo "Hello world!" > test.txt && ( TEST="$(cat test.txt)" ; echo "$TEST" )
使用該分號,TEST="$(cat test.txt)"
將不會評估 ' ' 的結果來確定是否 ' echo "$TEST"' will be run. When I see a &&
,我傾向於嘗試找出將要評估的內容以及為什麼這很重要。因此,儘管需要使用括號增加了複雜性,但在我看來,這種帶有分號的變體實際上在心理處理上可能更簡單一些。
答案2
它不起作用,因為$var
擴展是在命令列運行之前由 shell 執行的,即不是透過“回顯”命令。
同時,VAR=value somecommand
不是一個常規的賦值——它只是為了將環境變數注入到新進程中,但對創建該進程之前的shell變數沒有影響,即它與VAR=value; somecommand
.
換句話說,它發生的順序與您貼文標題描述的順序相反。首先 (stil-empty)"$TEST"
擴展為""
,然後 echo ""
TEST=...
使用它不關心的附加環境變數來運行。
由於您的測試命令應該模仿 Ansible 的行為,因此它們實際上應該查看他們的環境變數並且不依賴 shell 的預擴展;類似VAR=value env
or 的東西VAR=value printenv VAR
會比較合適。
答案3
為什麼以下指令在 bash 中不起作用?
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
如果你運行它ShellCheck – shell腳本分析工具它會告訴你原因:
$ shellcheck myscript
Line 2:
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" echo "$TEST"
^-- SC2097 (warning): This assignment is only seen by the forked process.
>> ^-- SC2098 (warning): This expansion will not see the mentioned assignment.
答案4
根據@DavidPostill 提供的鏈接,我將使用
echo "Hello world!" > test.txt && TEST="$(cat test.txt)" && echo "$TEST"
適當編輯
正如許多用戶正確指出的那樣,我的答案遠遠不是一個有用的答案:它不允許任何人了解主題的任何內容。
請參閱@TOOGAM 的郵政下面,這是最好的答案(我個人的意見),並充分揭示了為什麼@Peter Mortensen的程式碼不起作用,bash變數擴展如何工作,甚至為什麼我使用第二個&&
不是最佳選擇。