【问题标题】:Bash: Passing parameters to a C program via shell variable. How to deal with spaces and quoting characters?Bash:通过 shell 变量将参数传递给 C 程序。如何处理空格和引号字符?
【发布时间】:2020-01-25 16:01:01
【问题描述】:

我的 Bash 脚本 将一组(多个)参数发送到 C 程序。

这是我的C程序

$ cat main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( int argc, char **argv )
{

    printf("Parameter 1 is: %s \n", argv[1]);
    printf("Parameter 2 is: %s \n", argv[2]);
    return 0;
}

编译后(如MyCProgram),它的行为正常:

MyCProgram -f "/path/to/file/with spaces"
Parameter 1 is: -f
Parameter 2 is: /path/to/file/with spaces

但是,当尝试向它发送 通过 shell 变量的参数时:

$ var='-f "/path/to/file/with spaces" '
$ MyCProgram $var
Parameter 1 is: -f
Parameter 2 is: "/path/to/file/with
$ MyCProgram "$var"
Parameter 1 is: -f "/path/to/file/with spaces"
Parameter 2 is: (null)
$ MyCProgram '$var'
Parameter 1 is: $var
Parameter 2 is: (null)
$ MyCProgram "$(echo $var)"
Parameter 1 is: -f "/path/to/file/with spaces"
Parameter 2 is: (null)
$ MyCProgram "$(echo "$var")"
Parameter 1 is: -f "/path/to/file/with spaces"
Parameter 2 is: (null)
$ MyCProgram "$(echo '$var')"
Parameter 1 is: $var
Parameter 2 is: (null)
$ MyCProgram '$(echo "$var")'
Parameter 1 is: $(echo "$var")
Parameter 2 is: (null)
$ var="-f '/path/to/file/with spaces' "
$ MyCProgram $var
Parameter 1 is: -f
Parameter 2 is: '/path/to/file/with

我怎样才能获得正确的行为,这与在没有 shell 变量的情况下运行时相同?

注意事项

根据 Craig Estey 的回答,这很好用:

$ var=(-f "/file with spaces")
$ MyCProgram "${var[@]}"

此方法有效,但前提是我手动(明确)将值分配给var。假设 var 已经分配(即:通过输入读取或文件读取),我会说这里的问题是将其转移到 bash 数组变量。所以:

$ var='-f "/file with spaces"'
$ var2=( $var )
$ MyCProgram "${var2[@]}"
Parameter 1 is: -f
Parameter 2 is: "/file

问题依然存在。

【问题讨论】:

  • Assuming var is already assigned (i.e: via input read or file read) - 那么您应该修复“输入读取”或“文件读取”的方式,以便正确处理带空格的输入。这看起来像 XY 问题 - 您正在尝试在其他地方解决需要解决的问题。您在问如何解析带有" 封闭元素的字符串?编写您自己的解析器,它将以您想要的方式正确读取和转义它们。文件名中的不可打印字符应该怎么办?或者如果文件名包含 " 怎么办?
  • 您是否查看过特殊变量 $@ 以及您的脚本如何与用户交互?当我想将命令行参数从 shell 的命令行传递给脚本中的子命令时,我使用“$@”,以便在给子命令时引用给脚本的所有参数。
  • 引号是shell语法,但变量(和文件)中存储的是data,一般不被视为shell语法(例外:空格作为分隔符和通配符扩展——如果你不引用变量)。您要求的是更多类似于 shell 语法的数据解析。除非您非常清楚要应用哪些解析规则,否则这是相当危险的,而且往往很复杂且容易出错。这就引出了一个重要的问题:你在这里的实际目标是什么?为什么要尝试将 shell 语法与数据混合?有没有更好的办法?

标签: c bash shell parameters


【解决方案1】:

您可能想要使用 bash 数组 变量

执行以下设置变量:

var=(-f "/file with spaces")

然后,调用你的程序:

MyCProgram "${var[@]}"

【讨论】:

  • 谢谢,但是这个解决方案并不能完全解决问题。为原始问题添加了详细信息以反映。
  • 如果你从文件中读取变量,并将它传递给你的程序(例如myprogram "$var"),第一个参数将是整个行,程序将只接收一个 single 参数。这可以解决 if 您希望 myprogram parse 行并在内部进行参数拆分(即它处理带引号的字符串)。输入文件行本身需要采用以下形式:-f "/file with spaces"-f '/file with spaces'。这是你想做的吗?
  • 另一方面,让myprogram 读取文件并解析所有行可能更容易。看你怎么想了。
  • 我宁愿考虑其他一些解决方案,比如xargs 方式。无论如何,您转换为数组变量让我走上了一条好路,我已经用IFS=$'\n' varArray=( $(xargs -n1 &lt;&lt;&lt;"$var") )MyCProgram "${varArray[@]}" 解决了它(如其他cmets 所述)。谢谢你。
【解决方案2】:

var='-f "/path/to/file/with spaces" '

在这种情况下,var 的内容是 shell 语法。

为了处理shell语法,我们可以使用eval函数:

eval "MyProgram $var"

这将构造参数

MyProgram -f "/path/to/file/with spaces" 

然后将其评估为一个小 shell 脚本。

不用说,存在安全隐患。我们相信 var 中包含的脚本构建的输入。出于这个原因,eval 应该被尽可能地避开。

当我们引入eval 时,我们必须考虑:谁控制进入传递给eval 的字符串的输入?是否有人恶意潜入某些东西让eval 运行任意代码?

如果var 的构造在您的控制之下,并且没有不受信任的输入,或者这些输入得到了适当的处理,那么这可能是一个合适的解决方案。

【讨论】:

  • 经过测试可以同时使用 var="-f '/path/to/file/with spaces' "var='-f "/path/to/file/with spaces" ' 。谢谢你。
【解决方案3】:

如果在xargs 中不指定分隔符,它会解析"' 引号和反斜杠,因此假设var 内容正常,您可以这样做:

<<<"$var" xargs MyProgram

您可以为var 内容编写自己的标记器 - 例如。在第一个空格上拆分它,然后删除 " 引号。

# split var on space
IFS=' ' read -r option file <<<"$var"
# remove quotes from file - ie. remove last and first character
file=${file%\"}
file=${file#\"}
# run your program
MyProgram "$option" "$file"

【讨论】:

【解决方案4】:

请您尝试以下方法:

var='-f "/path/to/file/with spaces" '
declare -a var2="( $var )"
./MyCProgram "${var2[@]}"

输出:

Parameter 1 is: -f 
Parameter 2 is: /path/to/file/with spaces 

请注意,declare 可能与eval 一样具有潜在风险,您需要进行控制,以免将任意字符串馈送到declare

【讨论】:

  • 测试工作,tshiono。扩展一下declare -a 解决问题的原因会很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2014-10-04
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 1970-01-01
  • 2018-06-24
相关资源
最近更新 更多