如何防止我想要解析的变量周围的参数扩展?

如何防止我想要解析的变量周围的参数扩展?

编辑:

进一步阅读之前的重要说明:自拘萨罗南达的回答,我的问题现在毫无用处,因为他指出了我使用的另一个脚本中的错误。现在这个错误已修复,我描述的以下错误不再发生!

您的代码中有两个问题导致 $extension 中的值被扩展
第二个问题是函数本身


长话短说

当我开始写问题时,我被困住了,但找到了一个有效的解决方案(使用壳诺格洛布),但我仍然有一个问题“是否可以写”。

我确信这个问题已经得到解答,但我迷失在数百个有关 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)shell 扩展发生在ffmpeg接收参数之前(ffmpeg甚至不知道发生了 Shell 扩展)

${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 "$*" ;
    $* ;    
}

答案1

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

例子:

$ 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 "$*" ;
    $* ;    
}

在这里,您使用$*不加引号的方式,这会再次(将值拆分为单词,然后)扩展全局,即使您${extension}在函数调用中使用双引号也是如此。

$*使用"$@"(带双引号)代替裸露的。这将扩展到单独引用函数的位置参数。

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

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

答案2

阻止 shell 通配的方法是引用变量扩展。 (它还会停止分词,这通常也是您想要的)。当然,这并不能阻止命令本身处理类似 glob 的模式。ffmpeg为 执行此操作-i,例如 也是如此find -name。以后者为例:

$ touch a.foo b.foo c.bar
$ extension=foo
$ set -x
$ find . -name "*.$extension"
+ find . -name '*.foo'
./b.foo
./a.foo

(以 a 开头的行+来自set -x,并显示命令 shell实际上跑了。)

虽然您也可以停止使用 进行通配set -f,但它对分词没有帮助。如果变量包含空格,则未加引号的扩展将会中断,即使使用set -f.

答案3

当我发现执行此操作的替代语法时,我将其写在这里。

通过使用标志来禁用Shell 通配符http://tldp.org/LDP/abs/html/globbingref.html),它确实阻止了扩展:

set -f ; # disable glob (prevent expansion on '*.jpg' to turn in "1.jpg 2.jpg 3.jpg...")
ffmpeg -pattern_type glob -i ${extension} movie.mp4 ;
set +f ; # restore

相关内容