Bash

如何防止圍繞我要解析的變數進行參數擴展?

  • August 17, 2021

編輯:

進一步閱讀之前的重要說明:自從Kusalananda 的 anwser以來,我的問題現在已經毫無用處了,因為他指出了我使用的另一個腳本中的錯誤。現在這個bug被修復了,我描述的下面的錯誤不再發生了!

您的程式碼中有兩個問題導致 $extension 中的值被擴展

第二個問題是函式本身


TL;博士

當我開始寫這個問題時,我被卡住了,但找到了一個可行的解決方案(使用shell noglob),但我仍然有一個“是否可以寫”的問題。

我確信這已經得到解答,但是我迷失在數百個關於 bash 擴展的主題中。

這是我的情況:

  • 我想執行一個腳本(可以說帶有接受正則表達式ffmpeg的選項模式)-pattern_type glob -i``*.JPG
  • 爭論(假設*.JPG
  • 來自變數的參數(比如說$extension
  • 來自腳本參數的變數(比如說$1
  • 但我希望 shell$extension預設擴展為像這樣的模式*.JPG

請注意,所有這些操作都發生在腳本script.sh中。

因此,使用者可以執行:

script.sh 'JPG' ; # where $1='JPG'

注意:在 中script.sh,我連接*.$1獲得更最終的正則表達式,例如*.JPG

我讀到(從這裡https://stackoverflow.com/a/11456496/912046)外殼擴展發生在ffmpeg接收參數之前(ffmpeg甚至不知道外殼擴展發生)

我嘗試以不同的方式使用引號、雙引號或反斜杠${extension},但沒有成功。

以下是一些嘗試:

ffmpeg ... "${extension}"

導致:(ffmpeg 1.jpg 2.jpg [...]發生擴展)

ffmpeg ... ${extension}

相同的

ffmpeg ... '${extension}'

由於搜尋模式導致沒有文件匹配${extension}(按字面意思使用變數名)

extension="\'${extension}\'" ; # wrapping the single quotes (preventing expansion) directly in variable
ffmpeg ... ${extension}

'*.jpg'由於搜尋模式(包括單引號)導致沒有文件匹配

ffmpeg ... \"${extension}\"

相同但搜尋"*.jpg"(包括雙引號)

我終於成功使用了 Shell noglob 選項。

這是好奇的命令:

set -f ; # disable glob (prevent expansion on '*.jpg')
ffmpeg -pattern_type glob -i ${extension} movie.mp4 ;
set +f ; # restore, but what if glob was already disabled? I should not restore it then?

但是出於我的好奇心,是否有另一種方法可以防止來自我們想要解決的變數的腳本參數擴展?(因此,我知道防止擴展(使用單引號)的方式使變數不再解析/替換)。

類似的東西:(但我讀到應該避免字元串連接以確保安全,注入)

ffmpeg '${extension}'
      ||           | 3
      || 2
      | 1

在哪裡 :

  • 1:將為最終結果打開單引號ffmpeg '*.jpg'
  • 2:將我們的變數注入並解析extension.jpg
  • 3:會關閉單引號ffmpeg來接收參數,就像我手動寫的一樣'*.jpg'

編輯:在評論如何分配擴展變數後:

local extension="${2}" ;
echo $extension ;
extension="${extension:=jpg}" ; # Default on "jpg" extension
# wrapp extension type in single quotes + prefix with "*."
extension="*.${extension}" ;

然後,將其用於:

runprintcommand ffmpeg -loglevel verbose -pattern_type glob -i ${extension} "$movieName" ;

runprintcommand函式/腳本在哪裡:

function runprintcommand() {
   echo "Command to run: (Note: '\\' escape character may not be printed)" ;
   echo "$*" ;
   $* ;    
}
  • 未加引號的變數將經歷變數擴展、單詞拆分(預設情況下在空格、製表符和換行符上),並且每個生成的單詞將依次進行文件名生成(萬用字元)。
  • 雙引號內的變數將擴展其值,但外殼程序不會對擴展值進行分詞或通配。
  • 單引號內的變數根本不會被擴展。

例子:

$ ls
script.sh
$ var='* *'
$ echo $var
script.sh script.sh
$ echo "$var"
* *
$ echo '$var'
$var

您的程式碼中有兩個問題會導致 in 的值$extension擴展為它包含的 glob 模式,並且還會導致 glob 模式過早地與文件名匹配(您希望將其按原樣交給ffmpeg -i它自己的 glob 擴展內部)。

首先是您對函式的呼叫:

runprintcommand ffmpeg -loglevel verbose -pattern_type glob -i ${extension} "$movieName" ;

在這裡,未加${extension}引號,因此您肯定會在其值上發生(分詞和)文件名萬用字元。

第二個問題是函式本身:

function runprintcommand() {
   echo "Command to run: (Note: '\\' escape character may not be printed)" ;
   echo "$*" ;
   $* ;    
}

在這裡,您使用$*unquoted,即使您${extension}在對函式的呼叫中使用雙引號,它也會(將值拆分為單詞然後)擴展 glob。

$*使用"$@"(帶雙引號)而不是裸露的 。這將擴展到函式的單獨引用的位置參數。

"$@"這是和之間的區別"$*"

  • "$*"一個雙引號字元串。這通常不能用於執行帶參數的命令。
  • "$@"一個雙引號單詞列表。這可用於執行帶有參數的命令。

引用自:https://unix.stackexchange.com/questions/504808