用于更新postgres数据库的bash脚本

时间:2012-11-17 23:51:06

标签: bash postgresql shell cat psql

我现在有一些html数据存储在文本文件中。我最近决定将HTML数据存储在pgsql数据库中而不是平面文件中。现在,'entries'表包含一个指向文件的'path'列。我添加了一个'content'列,现在应该将数据存储在'path'指向的文件中。完成后,“路径”列将被删除。我遇到的问题是文件包含撇号,使我的脚本失败。我该怎么做才能纠正这个问题?

这是脚本

#!/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时,编辑中的任何撇号: $ f的内容会关闭SQL字符串,并且会出现语法错误。

2 个答案:

答案 0 :(得分:2)

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

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

包含文件内容而不是cat,以及LIKE中您打算使用文件 name <的第二次调用/ em>使用:

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

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

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

如果您需要担心其他炮弹。


SQL中存在许多其他问题。

  • 您正在尝试在第二次调用中执行 $f。我很确定你不是故意的;我想你想要包含文字字符串。
  • 你的子查询也错了,它没有括号; (SELECT ...)不只是SELECT
  • 你的LIKE表达也可能没有按照你的意图行事;你可能意味着%而不是*,因为%是SQL通配符。

如果我也将反引号更改为$()(因为它更清晰,更容易阅读IMO),修复子查询语法并添加别名以消除列的歧义,并使用here-document代替传递给{{1 stdin,结果是:

psql

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


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

例如,我写如下:

'O''Brien'

请注意,SQL通配符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中会产生一个%。这是因为Python使用%作为其格式说明符,因此文字%必须加倍才能使其转义。

您可以简单地修改上面的脚本以接受文件名列表,连接到数据库一次,并循环遍历所有文件名列表。那将是 lot 更快,特别是如果你在一个交易中完成所有这些。使用%脚本执行此操作真的很痛苦;你必须使用bash协同进程as shown here ......这不值得麻烦。

答案 1 :(得分:0)

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

为了更清楚,我的文件内容被格式化为html片段,通常类似于<p>Blah blah <b>blah</b>...</p>。在尝试了Craig发布的解决方案后,我意识到我在一些锚标签中使用了单引号,我不想将其更改为其他内容。只有少数文件发生了这种违规行为,所以我只是手动将这些文件更改为双引号。我也意识到,不是转义撇号,最好将它们转换为&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

这里的格式着色可能会使它看起来像语法不正确,但我已经确认它是正确的发布。