我似乎陷入了矛盾的境地。我有一个长字符串存储在变量$abstract
中,如下所示:
abstract='test1 and "test2"'
你看到字符串中有一个引号,我想把这个字符串作为一个参数发送给一个命令,我尝试了以下命令:
command_name "$abstract"
在变量替换之后,变为:command_name test1 and "test2"
。如果我尝试:
command_name \"$abstract\"
它变为:command_name "test1 and "test2""
。两者都不是我想要的。谁能告诉我如何实现我的目标?
答案 0 :(得分:8)
你的第一次尝试是正确的:
command_name "$abstract"
使用单个参数command_name
执行test1 and "test2"
。例如:
:; mkdir empty
:; cd empty
:; abstract='test1 and "test2"'
:; touch "$abstract"
:; ls -l
total 0
-rw-r--r-- 1 mayoff wheel 0 Mar 2 21:26 test1 and "test2"
您可以看到touch
仅创建了一个文件,并将其命名为test1 and "test2"
。
因此,根据您的注释,您实际上希望将shell变量插入到SQL语句中,以将命令行传递给sqlite3
。
首先,您应该知道使用"
引用SQLite3中的字符串是危险的。它被允许作为正常规则的例外,如“SQLite Keywords” documentation中所述,它还说“SQLite的未来版本可能会更改以引发错误,而不是接受上述例外所涵盖的格式错误的语句。”
因此,如果我们按照预期使用单引号,您需要执行此操作:
sqlite3 test.db "insert into test values('$abstract')"
当然,使用您为$abstract
提供的示例值可以正常工作。
让我们改为更具挑战性的$abstract
版本:
abstract="'test' and \"test2\""
为了解决这个问题,我们需要在SQLite看到之前引用单引号。在SQLite3字符串中,一行中的两个单引号表示一个单引号。也就是说,我们想运行这个SQLite3命令:
insert into test values('''test'' and "test2"')
无论如何,bash实际上有一种方便的方法。在bash中,您可以说${variable//PATTERN/STRING}
,bash会将其扩展为$variable
的值,但它会在扩展中用PATTERN
替换STRING
的每个实例。我们可以用它来用$abstract
替换两个单引号中的每个单引号。但是单引号在PATTERN
出现时很难用,所以我们必须用反斜杠引用那里的单引号。但我们不会在替换STRING
中引用单引号。哇,这让人感到困惑,不是吗?
无论如何,你正在寻找的神奇咒语是:
sqlite3 test.db "insert into test values('${abstract//\'/''}')"
我们可以使用touch
:
:; mkdir empty
:; cd empty
:; touch sqlite3 test.db "insert into test values('${abstract//\'/''}')"
:; ls -l
total 0
-rw-r--r-- 1 mayoff wheel 0 Mar 3 15:01 insert into test values('''test'' and "test2"')
-rw-r--r-- 1 mayoff wheel 0 Mar 3 15:01 sqlite3
-rw-r--r-- 1 mayoff wheel 0 Mar 3 15:01 test.db
当然我们可以用SQLite测试它:
:; rm -f test.db
:; sqlite3 test.db 'create table test (x)'
:; sqlite3 test.db "insert into test values('${abstract//\'/''}')"
:; sqlite3 test.db 'select * from test'
'test' and "test2"
答案 1 :(得分:3)
如果您对rob mayoff的回答的评论是正确的,那么原始描述是错误的;你不是试图用一个参数运行命令,你试图用三个参数运行它:“test1”,“and”和“test2”(引号不是实际参数的一部分)。在这种情况下,您需要使用不同的方法,因为将引号放入变量中并没有做任何有用的事情。通常,执行此类操作的最佳方法是将所需的参数放入数组中,然后使用"${arrayname[@]}"
惯用法将其作为一系列参数传递给命令:
$ function printargs { printf "argument: '%s'\n" "$@"; }
$ abstract=(test1 and "test2")
$ printargs "${abstract[@]}"
argument: 'test1'
argument: 'and'
argument: 'test2'
$ args=(test.db "insert into test values(...,\"$abstract\")")
$ printargs "${args[@]}"
argument: 'test.db'
argument: 'insert into test values(...,"test1")'
答案 2 :(得分:1)
"$abstract"
与$abstract
和\"$abstract\"
之间的差异为:
"$abstract"
扩展为 test1 and "test2"
作为一个单词$abstract
扩展为 test1 and "test2"
,然后拆分为( test1
, and
, "test2"
)为3个字\"$abstract\"
只是3个项目的字符串连接( \"
, $abstract
, \"
),在展开 $abstract
之后,它变为( "test1
, { {1}} , and
)为3个字