如何在 Linux Bash 腳本中使用 eval

已發表: 2022-08-22
顯示 bash 提示符的 Linux 筆記本電腦
fatmawati achmad zaenuri/Shutterstock.com

在所有 Bash 命令中,可憐的舊eval可能名聲最差。 有道理,還是只是壞消息? 我們討論了這個最不受歡迎的 Linux 命令的用途和危險。

我們需要談談評估

使用不慎, eval會導致不可預知的行為甚至系統不安全。 從它的聲音來看,我們可能不應該使用它,對吧? 不完全是。

你可以對汽車說類似的話。 在壞人手中,它們是致命的武器。 人們在襲擊和逃跑中使用它們。 我們都應該停止使用汽車嗎? 不,當然不是。 但它們必須被正確使用,並且由知道如何駕駛它們的人使用。

如何在 Bash 中使用變量
相關如何在 Bash 中使用變量

用於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 "

使用帶有字符串變量的 eval 來計算文件中的單詞

該命令在當前 shell 中執行,而不是在子 shell 中。 我們可以很容易地證明這一點。 我們有一個名為“variables.txt”的短文本文件。 它包含這兩行。

 首先=操作方法
第二=極客

我們將使用cat將這些行發送到終端窗口。 然後我們將使用eval來評估cat命令,以便執行文本文件中的指令。 這將為我們設置變量。

 貓變量.txt
eval "$(cat variables.txt)"
迴聲 $first $second 

在當前 shell 中訪問由 eval 設置的變量

通過使用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 

使用 chmod 使腳本可執行

您需要對從本文複製的任何腳本執行此操作。 只需在每種情況下使用適當的腳本名稱即可。

當我們運行我們的腳本時,我們會看到來自變量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 個變量,稱為x1x10 。 循環體中的eval語句提供“x”並獲取循環計數器$n的值來創建變量名。 同時,它將新變量設置為循環計數器$n的值。

Linux Bash 腳本中 for 循環的 9 個示例
相關Linux Bash 腳本中 for 循環的 9 個示例

它將新變量打印到終端窗口,然後用新變量的值增加total變量。

在循環之外,再次打印 10 個新變量,全部在一行上。 請注意,我們也可以通過它們的真實名稱來引用變量,而無需使用它們名稱的計算或派生版本。

最後,我們打印total變量的值。

 ./loop.sh 

使用eval動態創建變量

相關:入門: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指示的位置。 該名稱將打印到終端窗口。 在真實世界的腳本中,您還需要創建實際文件。

如何使用 Linux 睡眠命令暫停 Bash 腳本
相關如何使用 Linux Sleep 命令暫停 Bash 腳本

腳本的主體是使用sleep命令模擬的。 它創建第一個日誌文件,等待三秒鐘,然後創建另一個。 它創建了四個日誌文件,這些文件相互隔開,以使它們文件名中的時間戳不同。

最後,有一個刪除日誌文件的循環。 循環計數器文件設置為 1。 它計數到並包括filecount的值,該值保存已創建的文件數。

如果filecount仍然設置為零——因為沒有創建日誌文件——循環體將永遠不會被執行,因為 1 不小於或等於零。 這就是為什麼filecount變量在聲明時設置為零以及為什麼在創建第一個文件之前遞增的原因。

在循環內部,我們將eval與我們的非破壞性rm_string和從數組中檢索到的文件的名稱一起使用。 然後我們將數組元素設置為空字符串。

這是我們在運行腳本時看到的。

 ./clear-logs.sh 

刪除名稱存儲在數組中的文件

不全是壞事

備受詬病的eval肯定有它的用途。 像大多數工具一樣,魯莽使用它是危險的,而且方式不止一種。

如果您確保它所處理的字符串是在內部創建的,而不是從人類、API 或 HTTPS 請求之類的東西中捕獲的,那麼您將避免主要的陷阱。

相關:如何在 Linux 終端中顯示日期和時間(並在 Bash 腳本中使用它)