【问题标题】:bash script to update postgres database用于更新 postgres 数据库的 bash 脚本
【发布时间】:2012-11-17 23:51:06
【问题描述】:

我现在有一些 html 数据存储在文本文件中。我最近决定将 HTML 数据存储在 pgsql 数据库中而不是平面文件中。现在,“条目”表包含指向文件的“路径”列。我添加了一个“内容”列,现在应该将数据存储在“路径”指向的文件中。完成后,“路径”列将被删除。我遇到的问题是这些文件包含使我的脚本无法正常工作的撇号。我可以做些什么来纠正这个问题??

这是脚本

#!/bin/sh
dbname="myDB"
username="username"
fileroot="/path/to/the/files/*"

for f in $fileroot
do
psql $dbname $username -c "
  UPDATE entries
  SET content='`cat $f`'
  WHERE id=SELECT id FROM entries WHERE path LIKE '*`$f`';"
done

注意:id=SELECT...FROM...WHERE path LIKE "" 中的逻辑不是问题所在。我已经在 pgsql 环境中使用示例文件名对此进行了测试。

问题是,当我cat $f时,Edit: the contents of $f 中的任何撇号都会关闭 SQL 字符串,并且出现语法错误。

【问题讨论】:

    标签: bash postgresql shell cat psql


    【解决方案1】:

    对于单引号转义问题,合理的解决方法可能是将引号加倍,因此您可以使用:

    `sed "s/'/''/g" < "$f"`
    

    包含文件 contents 而不是 cat,并且对于 LIKE 中的第二次调用,您似乎打算使用文件 name 使用:

    ${f/"'"/"''"/}
    

    包含$f 的文字字符串内容而不是执行它,并将引号加倍。 ${varname/match/replace} 表达式是 bash 语法,可能不适用于所有 shell;使用:

    `echo "$f" | sed "s/'/''/g"`
    

    如果您需要担心其他 shell。


    该 SQL 中还有许多其他问题。

    • 您正在尝试在第二次调用中执行$f。我很确定你不是故意的。我想你的意思是包含文字字符串。
    • 您的子查询也是错误的,它缺少括号; (SELECT ...) 不仅仅是SELECT
    • 您的LIKE 表达式也可能没有达到您的预期;您可能指的是% 而不是*,因为% 是SQL 通配符。

    如果我还将反引号更改为 $()(因为它更清晰、更易于阅读 IMO),请修复子查询语法并添加别名以消除列的歧义,并使用此处的文档而不是传递给 psql's标准输入,结果是:

    psql $dbname $username <<__END__
      UPDATE entries
      SET content=$(sed "s/'/''/g" < "$f")
      WHERE id=(SELECT e.id FROM entries e WHERE e.path LIKE '$(echo "$f" | sed "s/'/''/g")');
    __END__
    

    以上假设您正在使用带有standard_conforming_strings = on 的相当现代的PostgreSQL。如果不是,请更改正则表达式以使用\ 转义撇号,而不是将它们加倍,并在字符串前面加上E,因此O'Brien 变为E'O\'Brien'。在现代 PostgreSQL 中,它会变成 'O''Brien'


    一般来说,我建议使用真正的脚本语言,例如带有 DBD::Pg 的 Perl 或带有 psycopg 的 Python 来解决数据库的脚本问题。使用 shell 有点时髦。使用支持参数化语句的数据库接口编写此表达式会更容易。

    例如,我会这样写:

    import os
    import sys
    import psycopg2
    
    try:
            connstr = sys.argv[1]
            filename = sys.argv[2]
    except IndexError as ex:
            print("Usage: %s connect_string filename" % sys.argv[0])
            print("Eg: %s \"dbname=test user=fred\" \"some_file\"" % sys.argv[0])
            sys.exit(1)
    
    
    def load_file(connstr,filename):
            conn = psycopg2.connect(connstr)
            curs = conn.cursor()
            curs.execute("""
            UPDATE entries
            SET content = %s
            WHERE id = (SELECT e.id FROM entries e WHERE e.path LIKE '%%'||%s);
            """, (filename, open(filename,"rb").read()))
            curs.close()
    
    if __name__ == '__main__':
            load_file(connstr,filename)
    

    注意 SQL 通配符 % 加倍以对其进行转义,因此最终 SQL 中只有一个 %。这是因为 Python 使用 % 作为其格式说明符,因此必须将文字 % 加倍才能转义。

    您可以简单地修改上述脚本以接受文件名列表,连接到数据库一次,然后遍历所有文件名列表。这会快很多很多,尤其是如果您在一次交易中完成所有操作。用psql 脚本来做这件事真的很痛苦;你必须使用 bash co-process as shown here ... 而且不值得这么麻烦。

    【讨论】:

    • 回答了我可能想知道的一切。谢谢。
    【解决方案2】:

    在原始帖子中,我听起来好像 $f 表示的文件名中有撇号。事实并非如此,所以一个简单的echo "$f" 就能解决我的问题。

    为了更清楚,我的文件内容被格式化为 html sn-ps,通常类似于 &lt;p&gt;Blah blah &lt;b&gt;blah&lt;/b&gt;...&lt;/p&gt;。在尝试了 Craig 发布的解决方案后,我意识到我在一些锚标签中使用了单引号,我不想将它们更改为其他东西。只有少数文件发生了这种违规行为,所以我只是手动将它们更改为双引号。我还意识到,与其转义撇号,不如将它们转换为&amp;apos; 这是我最终使用的最终脚本:

    dbname="myDB"
    username="username"
    fileroot="/path/to/files/*"
    
    for f in $fileroot
    do
    psql $dbname $username << __END__
      UPDATE entries
      SET content='$(sed "s/'/\&apos;/g" < "$f")'
      WHERE id=(SELECT e.id FROM entries e WHERE path LIKE '%$(echo "$f")');
    __END__
    done
    

    此处的格式着色可能使它看起来语法不正确,但我已经验证它是正确的。

    【讨论】:

      猜你喜欢
      • 2016-01-28
      • 1970-01-01
      • 2013-08-15
      • 1970-01-01
      • 2019-07-30
      • 2020-05-26
      • 2014-08-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多