对于单引号转义问题,合理的解决方法可能是将引号加倍,因此您可以使用:
`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 ... 而且不值得这么麻烦。