為什麼使用命令替換分配變量,然後回顯該變量總是失敗?

為什麼使用命令替換分配變量,然後回顯該變量總是失敗?

為什麼以下指令在 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 確實執行了將該命令分解為多個部分的操作。此操作會刪除引號。所以各個部分看起來像這樣:

  1. 測試
  2. =
  3. 你好世界!
  4. 迴音
  5. (空值)

好的,現在 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 提供的答案顯示了一些有點浪費的代碼,儘管這是可以理解的,因為它確實直接回答了所提出的問題。它的基本作用是:

首先,將命令分為三個部分:

  • 迴聲“世界你好!” > 測試.txt
  • 測試=“$(貓測試.txt)”
  • 回顯“$測試”
  • 第三部分僅在第二部分(運行並)成功時運行,而第二部分僅在第一個命令成功時運行,因為有兩個&&運算符。

    另一種變化可能是:

    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 envor 的東西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.
    

    ShellCheck:SC2097 – 此分配僅可由分叉程序看到。

    答案4

    根據@DavidPostill 提供的鏈接,我將使用

    echo "Hello world!" > test.txt && TEST="$(cat test.txt)" && echo "$TEST"
    

    適當編輯
    正如許多用戶正確指出的那樣,我的答案遠遠不是一個有用的答案:它不允許任何人了解主題的任何內容。
    請參閱@TOOGAM 的郵政下面,這是最好的答案(我個人的意見),並充分揭示了為什麼@Peter Mortensen的程式碼不起作用,bash變數擴展如何工作,甚至為什麼我使用第二個&&不是最佳選擇。

    相關內容