如何在 Linux Bash 腳本中使用 eval
已發表: 2022-08-22
在所有 Bash 命令中,可憐的舊eval
可能名聲最差。 有道理,還是只是壞消息? 我們討論了這個最不受歡迎的 Linux 命令的用途和危險。
我們需要談談評估
使用不慎, eval
會導致不可預知的行為甚至系統不安全。 從它的聲音來看,我們可能不應該使用它,對吧? 不完全是。
你可以對汽車說類似的話。 在壞人手中,它們是致命的武器。 人們在襲擊和逃跑中使用它們。 我們都應該停止使用汽車嗎? 不,當然不是。 但它們必須被正確使用,並且由知道如何駕駛它們的人使用。
用於eval
的常用形容詞是“邪惡”。 但這一切都取決於它的使用方式。 eval
命令整理來自一個或多個變量的值。 它創建一個命令字符串。 然後它執行該命令。 當您需要處理在腳本執行期間動態派生命令內容的情況時,這非常有用。
當編寫腳本以對從腳本外部某處接收到的字符串使用eval
時,就會出現問題。 它可以由用戶輸入、通過 API 發送、標記到 HTTPS 請求或腳本外部的任何其他位置。
如果eval
將要處理的字符串不是在本地和以編程方式派生的,則該字符串可能包含嵌入的惡意指令或其他格式錯誤的輸入。 顯然,您不希望eval
執行惡意命令。 所以為了安全起見,不要將eval
與外部生成的字符串或用戶輸入一起使用。
eval 的第一步
eval
命令是一個內置的 Bash shell 命令。 如果存在 Bash,則將存在eval
。
eval
將其參數連接成一個字符串。 它將使用單個空格來分隔連接的元素。 它評估參數,然後將整個字符串傳遞給 shell 以執行。
讓我們創建一個名為wordcount
的變量。
wordcount="wc -w raw-notes.md"
字符串變量包含一個命令,用於計算名為“raw-notes.md”的文件中的單詞。
我們可以使用eval
通過將變量的值傳遞給它來執行該命令。
eval " $wordcount "
該命令在當前 shell 中執行,而不是在子 shell 中。 我們可以很容易地證明這一點。 我們有一個名為“variables.txt”的短文本文件。 它包含這兩行。
首先=操作方法 第二=極客
我們將使用cat
將這些行發送到終端窗口。 然後我們將使用eval
來評估cat
命令,以便執行文本文件中的指令。 這將為我們設置變量。
貓變量.txt eval "$(cat variables.txt)" 迴聲 $first $second
通過使用echo
打印變量的值,我們可以看到eval
命令在當前 shell 中運行,而不是在子 shell 中。
子shell 中的進程不能更改父shell 的shell 環境。 因為 eval 在當前 shell 中運行,所以eval
設置的變量可以從啟動eval
命令的 shell 中使用。
請注意,如果您在腳本中使用eval
,那麼將被eval
更改的 shell 是腳本正在運行的子 shell,而不是啟動它的 shell。
相關:如何使用 Linux cat 和 tac 命令
在命令字符串中使用變量
我們可以在命令字符串中包含其他變量。 我們將設置兩個變量來保存整數。
數字1=10 數字2=7
我們將創建一個變量來保存一個expr
命令,該命令將返回兩個數字的和。 這意味著我們需要訪問命令中兩個整數變量的值。 注意expr
語句周圍的反引號。
add="`expr $num1 + $num2`"
我們將創建另一個命令來顯示expr
語句的結果。
顯示=“迴聲”
請注意,我們不需要在echo
字符串的末尾包含空格,也不需要在expr
字符串的開頭包含空格。 eval
負責這一點。
並執行我們使用的整個命令:
評估$顯示$添加
expr
字符串中的變量值由eval
替換到字符串中,然後將其傳遞給 shell 以執行。
相關:如何在 Bash 中使用變量
訪問變量內部的變量
您可以為變量分配一個值,然後將該變量的名稱分配給另一個變量。 使用eval
,您可以訪問第一個變量中保存的值,其名稱是存儲在第二個變量中的值。 一個例子將幫助你解開這個問題。
將此腳本複製到編輯器,並將其保存為名為“assign.sh”的文件。
#!/bin/bash title="如何極客" 網頁=標題 命令=“迴聲” 評估 $command \${$webpage}
我們需要使用chmod
命令使其可執行。
chmod +x 分配.sh

您需要對從本文複製的任何腳本執行此操作。 只需在每種情況下使用適當的腳本名稱即可。
當我們運行我們的腳本時,我們會看到來自變量title
的文本,即使eval
命令使用了變量webpage
。
./assign.sh
轉義的美元符號“ $
”和大括號“ {}
”導致 eval 查看名稱存儲在webpage
變量中的變量中保存的值。
使用動態創建的變量
我們可以使用eval
動態創建變量。 該腳本稱為“loop.sh”。
#!/bin/bash 總計=0 label="循環完成。總計:" 對於 {1..10} 中的 n 做 評估 x$n=$n 迴聲“循環”$x$n ((總計+=$x$n)) 完畢 迴聲 $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10 迴聲$標籤$總計
它創建了一個名為total
的變量,它保存了我們創建的變量值的總和。 然後它創建一個名為label
的字符串變量。 這是一個簡單的文本字符串。
我們將循環 10 次並創建 10 個變量,稱為x1
到x10
。 循環體中的eval
語句提供“x”並獲取循環計數器$n
的值來創建變量名。 同時,它將新變量設置為循環計數器$n
的值。
它將新變量打印到終端窗口,然後用新變量的值增加total
變量。
在循環之外,再次打印 10 個新變量,全部在一行上。 請注意,我們也可以通過它們的真實名稱來引用變量,而無需使用它們名稱的計算或派生版本。
最後,我們打印total
變量的值。
./loop.sh
相關:入門:Bash 循環:for、while 和 until
使用 eval 和數組
想像一個場景,您有一個長時間運行並為您執行一些處理的腳本。 它使用從時間戳創建的名稱寫入日誌文件。 有時,它會啟動一個新的日誌文件。 腳本完成後,如果沒有錯誤,它會刪除它創建的日誌文件。
您不希望它簡單地rm *.log
,您只希望它刪除它創建的日誌文件。 此腳本模擬該功能。 這是“clear-logs.sh”。
#!/bin/bash 聲明 -a 日誌文件 文件數=0 rm_string="迴聲" 功能創建日誌文件(){ ((++filecount)) 文件名=$(日期 +"%Y-%m-%d_%H-%M-%S").log 日誌文件[$filecount]=$filename echo $filecount "創建" ${logfiles[$filecount]} } # 腳本的主體。 這裡做了一些處理 # 定期生成日誌文件。 我們將模擬它 創建日誌文件 睡覺 3 創建日誌文件 睡覺 3 創建日誌文件 睡覺 3 創建日誌文件 # 有沒有要刪除的文件? for ((file=1; file<=$filecount; file++)) 做 # 刪除日誌文件 eval $rm_string ${logfiles[$file]} "已刪除..." 日誌文件[$file]="" 完畢
該腳本聲明了一個名為logfiles
的數組。 這將保存腳本創建的日誌文件的名稱。 它聲明了一個名為filecount
的變量。 這將保存已創建的日誌文件的數量。
它還聲明了一個名為rm_string
的字符串。 在現實世界的腳本中,這將包含rm
命令,但我們正在使用echo
,因此我們可以以非破壞性方式演示該原理。
函數create_logfile()
是每個日誌文件的命名位置和打開位置。 我們只是在創建filename ,並假裝它是在文件系統中創建的。
該函數遞增filecount
變量。 它的初始值為零,因此我們創建的第一個文件名存儲在數組中的位置一。 這是有意為之的,稍後見。
文件名是使用date
命令和“.log”擴展名創建的。 該名稱存儲在數組中由filecount
指示的位置。 該名稱將打印到終端窗口。 在真實世界的腳本中,您還需要創建實際文件。
腳本的主體是使用sleep
命令模擬的。 它創建第一個日誌文件,等待三秒鐘,然後創建另一個。 它創建了四個日誌文件,這些文件相互隔開,以使它們文件名中的時間戳不同。
最後,有一個刪除日誌文件的循環。 循環計數器文件設置為 1。 它計數到並包括filecount
的值,該值保存已創建的文件數。
如果filecount
仍然設置為零——因為沒有創建日誌文件——循環體將永遠不會被執行,因為 1 不小於或等於零。 這就是為什麼filecount
變量在聲明時設置為零以及為什麼在創建第一個文件之前遞增的原因。
在循環內部,我們將eval
與我們的非破壞性rm_string
和從數組中檢索到的文件的名稱一起使用。 然後我們將數組元素設置為空字符串。
這是我們在運行腳本時看到的。
./clear-logs.sh
不全是壞事
備受詬病的eval
肯定有它的用途。 像大多數工具一樣,魯莽使用它是危險的,而且方式不止一種。
如果您確保它所處理的字符串是在內部創建的,而不是從人類、API 或 HTTPS 請求之類的東西中捕獲的,那麼您將避免主要的陷阱。
相關:如何在 Linux 終端中顯示日期和時間(並在 Bash 腳本中使用它)