如何防止围绕我要解析的变量进行参数扩展?

el-*_*dee 3 bash shell-script quoting variable bash-expansion

编辑:

进一步阅读之前的重要说明:由于Kusalananda 的 anwser,我的问题现在毫无用处,因为他指出了我使用的另一个脚本中的错误。现在此错误已修复,不再出现我描述的以下错误!

您的代码中有两个问题导致 $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'
Run Code Online (Sandbox Code Playgroud)

注意:在script.sh,我连接*.并$1获得更最终的正则表达式,如*.JPG

我读到(从这里/sf/answers/801954751/)外壳扩展发生在ffmpeg接收参数之前(ffmpeg甚至不知道外壳扩展发生)

我试图以${extension}不同的方式使用引号、双引号或反斜杠,但没有成功。
以下是一些尝试:

ffmpeg ... "${extension}"
Run Code Online (Sandbox Code Playgroud)

导致:(ffmpeg 1.jpg 2.jpg [...]发生膨胀)

ffmpeg ... ${extension}
Run Code Online (Sandbox Code Playgroud)

相同的

ffmpeg ... '${extension}'
Run Code Online (Sandbox Code Playgroud)

由于搜索模式导致没有文件匹配${extension}(变量名按字面使用)

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

由于搜索模式'*.jpg'(包括单引号)导致没有文件匹配

ffmpeg ... \"${extension}\"
Run Code Online (Sandbox Code Playgroud)

相同但正在搜索"*.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?
Run Code Online (Sandbox Code Playgroud)

但是出于我的好奇,是否有另一种方法可以防止来自我们想要解析的变量的脚本参数的扩展?(因此,我所知道的防止扩展(使用单引号)的方式使变量不再解析/替换)。

类似的东西:(但我读到为了安全,注入应该避免字符串连接)

ffmpeg '${extension}'
       ||           | 3
       || 2
       | 1
Run Code Online (Sandbox Code Playgroud)

在哪里 :

  • 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}" ;
Run Code Online (Sandbox Code Playgroud)

然后,使用它:

runprintcommand ffmpeg -loglevel verbose -pattern_type glob -i ${extension} "$movieName" ;
Run Code Online (Sandbox Code Playgroud)

runprintcommand函数/脚本在哪里:

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

Kus*_*nda 7

  • 未加引号的变量将进行变量扩展、分词(默认情况下在空格、制表符和换行符上),并且每个生成的单词将依次进行文件名生成(通配符)。
  • 双引号内的变量将扩展其值,但 shell 不会对扩展值进行分词或通配符。
  • 单引号内的变量根本不会被扩展。

例子:

$ ls
script.sh
Run Code Online (Sandbox Code Playgroud)
$ var='* *'
Run Code Online (Sandbox Code Playgroud)
$ echo $var
script.sh script.sh
Run Code Online (Sandbox Code Playgroud)
$ echo "$var"
* *
Run Code Online (Sandbox Code Playgroud)
$ echo '$var'
$var
Run Code Online (Sandbox Code Playgroud)

您的代码中有两个问题会导致 in 的值$extension扩展为其包含的 glob 模式,并且还会导致 glob 模式过早地与文件名匹配(您希望按原样将ffmpeg -i其交给它自己的 glob 扩展内部)。

第一个是您对函数的调用:

runprintcommand ffmpeg -loglevel verbose -pattern_type glob -i ${extension} "$movieName" ;
Run Code Online (Sandbox Code Playgroud)

在这里,没有${extension}被引用,所以你肯定会在它的值上得到(分词和)文件名通配符。

第二个问题是函数本身:

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

在这里,您使用$*unquoted,即使您${extension}在调用函数时使用双引号,它也会(将值拆分为单词然后)扩展 glob 。

而不是一个空的$*,使用"$@"(带双引号)。这将扩展到函数的单独引用的位置参数。

这之间的区别"$@"和"$*":

  • "$*"是一个双引号字符串。这通常不能用于执行带参数的命令。
  • "$@"是一个双引号词列表。这可用于执行带参数的命令。