bash字符串引用

时间:2012-03-03 03:23:50

标签: bash

我似乎陷入了矛盾的境地。我有一个长字符串存储在变量$abstract中,如下所示:

abstract='test1 and "test2"'

你看到字符串中有一个引号,我想把这个字符串作为一个参数发送给一个命令,我尝试了以下命令:

command_name "$abstract"

在变量替换之后,变为:command_name test1 and "test2"。如果我尝试:

command_name \"$abstract\"

它变为:command_name "test1 and "test2""。两者都不是我想要的。谁能告诉我如何实现我的目标?

3 个答案:

答案 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个字