【问题标题】:build bash string containing a variable value surrounded with single quotes构建包含用单引号括起来的变量值的bash字符串
【发布时间】:2012-11-16 10:27:59
【问题描述】:

我正在做一场噩梦,这应该是最微不足道的任务。

我的最终目标是从 bash 脚本发出以下命令:
sqlite3 my_db.db '.read my_file.sql'

这里有两个问题:
1. 单引号是必须的,不能用双引号代替
2. my_file.sql 是一个仅在运行时才知道的变量。

所以我需要一种方法让 bash 构建一个字符串,该字符串一方面包含一个变量值,另一方面该值应该用单引号括起来。

我也更喜欢不依赖于 AWK、Perl 等其他工具的解决方案。如果真的有必要,也许可以使用 sed。

谢谢。


感谢乔纳森和尼尔森。

我尝试了所有三个建议,但都失败了。
为简单起见,我将问题简化为以下内容:

  1. 我编写了以下脚本 (tst.sh):
    #!/bin/bash
    file=/tmp/1
    ls "'"$file"'"
    ls \'$file\'
    ls "'$file'"

  2. 然后我发出以下命令:
    $ touch /tmp/1
    $ ls '/tmp/1'
    /tmp/1
    $ ./tst.sh
    '/tmp/1': No such file or directory
    '/tmp/1': No such file or directory'/tmp/1': No such file or directory

似乎确实添加了引号,但生成的命令与手动输入的命令不同。

有什么想法吗?

【问题讨论】:

  • 单引号是强制性的原因是什么?那应该是单引号中的一个参数还是两个参数,一个以单引号开头,另一个以单引号结尾。

标签: string bash variables quote


【解决方案1】:

user1860085,如果您查看 sqlite3 命令的文档并且您会知道 shell 如何处理引号和空格,您可能会得出结论,您需要双引号。

但如果你真的想要单引号,这里是解决方案:

eval sqlite3 my_db.db \'.read $VARIABLE\'

其中会即时更改为:

sqlite3 my_db.db '.read my_file.sql'

但我不明白你为什么想要它......

【讨论】:

    【解决方案2】:

    单引号不是强制性的。以下所有命令都使用完全相同的参数运行 sqlite3:

    sqlite3 my_db.db '.read my_file.sql'
    sqlite3 my_db.db ".read my_file.sql"
    sqlite3 my_db.db .read\ my_file.sql
    sqlfile="my_file.sql"
    sqlite3 my_db.db ".read $sqlfile"
    

    在所有情况下,引号 (/escape) 在参数传递给 sqlite3 之前被解析并删除。这就是你想要的。您希望 sqlite3 获得两个参数:my_db.db.read my_file.sql。您确实希望 sqlite3 看到命令周围的引号 - 这相当于:

    $ sqlite3 my_db.db
    SQLite version 3.7.7 2011-06-25 16:35:41
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite> '.read my_file.sql'
       ...>
    

    ...如您所见,这只会让 sqlite3 感到困惑。

    顺便说一句,这与您的 ls 示例中的问题相同:您将单引号作为参数的一部分传递给 ls,因此它正在查找名称中包含单引号的文件而没有找到它。您希望 shell 删除引号,而不是将它们作为参数的一部分传递给命令。

    【讨论】:

    • 谢谢 Gordon - 这是我不知道的重要信息。不过,我的问题比 sqlite3 语法更广泛,从主题中可以看出。我相信我的最后一个答案会在许多其他情况下对其他人有所帮助。
    • @user1860085:深吸一口气,明白这里几位评论者反复试图解释的内容:你解决了错误的问题,你需要了解shell的引用机制。
    • 这个清晰的答案+1。
    【解决方案3】:

    好的,问题解决了!!

    所缺少的只是在该行之前添加一个小的“eval”命令。
    因此,在我给出的简单示例脚本中,将:
    ls "'$file'" 更改为:
    eval ls "'$file'"
    做了这项工作。

    感谢所有回复者:-)

    【讨论】:

    • 哎呀,不! eval 的主要目的是制造难以理解的错误。如果您确切地知道自己在做什么,就可以安全地使用它,但这不是其中一种情况。
    • 大声笑,抱歉,无意冒犯 ;-) 您能否详细说明您提到的 eval 的问题性质,以及它如何影响我的解决方案(这似乎有效..)?
    • 它展示了如何去掉两层引号,但最终结果完全等同于ls "$file"
    • 在需要引用的情况下,它不等同于 ls "$file"。例如,在我的原始示例中,当 <.read> 失败时它可以工作。
    • 您在这里所做的是在命令周围添加单引号,然后使用 eval 删除它们。说真的,这是eval 在这里做的唯一事情。如果它做其他事情,那将是一件坏事。在这种特殊情况下,eval 只会在文件名包含单引号时引起问题(在这种情况下 sqlite3 无论如何都会有问题),但您最好首先避免该问题。是什么让您认为无论如何都需要单引号?我很确定您在使用 sqlite3 my_db.db ".read $file" 时遇到的任何问题都不是因为缺少单引号。
    【解决方案4】:

    你可以这样做:

    VAR=my_file.sql
    VAR2="'.read $VAR'"
    sqlite3 my_db.db $VAR2
    

    【讨论】:

    • 或者没有变量 sqlite3 my_db.db "'.read my_file.sql'" 但我怀疑这实际上是解决方案,尽管这是 OP 所要求的。
    【解决方案5】:

    这将做你说你想做的事(为程序获取单引号),但它使用双引号:

    sqlite3 my_db.db "'".read" "my_file.sql"'"
    

    避免双引号,可以这样写:

    sqlite3 my_db.db \'.read\ my_file.sql\'
    

    对于这两个参数,sqlite3 会将第二个参数视为包含以下内容的字符串:

    '.read my_file.sql'
    

    如果文件名在变量中(file=my_file.sql),那么:

    sqlite3 my_db.db "'".read" "$file"'"
    sqlite3 my_db.db \'.read\ $file\'
    

    如果文件名包含空格,这些符号很容易混淆。

    但是,我认为这可能不是您真正想要的。双引号的禁止令人费解,单引号的要求同样令人费解。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-27
      • 2015-01-30
      • 2016-10-07
      • 2017-06-06
      • 2023-02-22
      • 1970-01-01
      相关资源
      最近更新 更多