簡體   English   中英

用於更新 postgres 數據庫的 bash 腳本

[英]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 發布的解決方案后,我意識到我在一些錨標簽中使用了單引號,我不想將它們更改為其他內容。 只有少數文件發生了這種違規,所以我只是手動將它們更改為雙引號。 我還意識到,與其轉義撇號,不如將它們轉換為&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

此處的格式着色可能會使語法看起來不正確,但我已驗證它在發布時是正確的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM