![](/img/trans.png)
[英]bash script : how to update database table using postgres psql and UPDATE statement
[英]bash script to update postgres database
我現在有一些 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
,編輯中的任何撇號: $f的內容會關閉 SQL 字符串,並且出現語法錯誤。
對於單引號轉義問題,合理的解決方法可能是將引號加倍,因此您可以使用:
`sed "s/'/''/g" < "$f"`
要包含文件內容而不是cat
,並且對於您似乎打算使用文件名的LIKE
中的第二次調用,請使用:
${f/"'"/"''"/}
包含$f
的文字字符串內容而不是執行它,並將引號加倍。 ${varname/match/replace}
表達式是bash
語法,可能不適用於所有 shell; 用:
`echo "$f" | sed "s/'/''/g"`
如果您需要擔心其他貝殼。
該 SQL 中還有很多其他問題。
$f
。 我很確定你不是故意的; 我想你的意思是包含文字字符串。(SELECT ...)
不僅僅是SELECT
。LIKE
表達也可能沒有按照你的意圖行事; 您可能指的是%
而不是*
,因為%
是 SQL 通配符。 如果我還將反引號更改為$()
(因為它更清晰,更容易閱讀 IMO),修復子查詢語法並添加別名以消除列歧義,並使用 here-document 代替傳遞給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__
以上假設您使用的是帶有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 協同處理,如此處所示......而且不值得麻煩。
在最初的帖子中,我聽起來像是 $f 表示的文件名中有撇號。 事實並非如此,因此一個簡單的echo "$f"
能夠解決我的問題。
為了更清楚,我的文件的內容被格式化為 html 片段,通常類似於<p>Blah blah <b>blah</b>...</p>
。 在嘗試了 Craig 發布的解決方案后,我意識到我在一些錨標簽中使用了單引號,我不想將它們更改為其他內容。 只有少數文件發生了這種違規,所以我只是手動將它們更改為雙引號。 我還意識到,與其轉義撇號,不如將它們轉換為'
這是我最終使用的最終腳本:
dbname="myDB"
username="username"
fileroot="/path/to/files/*"
for f in $fileroot
do
psql $dbname $username << __END__
UPDATE entries
SET content='$(sed "s/'/\'/g" < "$f")'
WHERE id=(SELECT e.id FROM entries e WHERE path LIKE '%$(echo "$f")');
__END__
done
此處的格式着色可能會使語法看起來不正確,但我已驗證它在發布時是正確的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.