這些 bash 字元串中的單詞可以有效地插入到 sqlite 表中嗎?
我有兩個 bash 變數
$FNAMES
,$LNAMES
我想插入到一個表中sqlite
,但我不太確定該怎麼做。這是一個mwe:
#!/usr/bin/env sh FNAMES="John Paul George Ringo" LNAMES="Lennon McCartney Harrison Starr" sqlite3 people.db "CREATE TABLE people(fname TEXT, lname TEXT);" sqlite3 people.db "INSERT INTO people(fname, lname) <MAGIC> ;" sqlite3 people.db -cmd ".mode column" "SELECT * FROM people;"
我希望這個腳本的輸出是:
fname lname ------ --------- John Lennon Paul McCartney George Harrison Ringo Starr
我假設我可以用一些東西代替
<MAGIC>
以獲得我想要的結果。這可能嗎?
sqlite3
命令行工具不支持prepared statements ,因此在shell腳本中執行此操作的任何嘗試都會遇到未轉義的引號等問題。在 shell/bash 中處理帶引號和不帶引號的變數已經有點痛苦了,而且當您使用具有自己的引用要求的 SQL 數據庫時,這種痛苦只會變得更糟。像這樣的任務應該使用具有 SQLite 庫的語言來完成。最常用的語言,無論是編譯的還是解釋的,都有這樣的庫。
下面是一些如何在 perl 中執行此操作的範例:
所有這些範例都需要安裝DBI和DBD::SQLite庫模組。如果您正在執行任何 Linux 發行版,這些幾乎肯定會以軟體包的形式提供。例如,在 Debian 上,執行
sudo apt install libdbd-sqlite3-perl
以安裝它們。大多數其他語言都有類似的庫。順便說一句,不使用 sh 或 bash(或其他 shell)的另一個原因是避免處理 shell 腳本中存在但其他語言中不存在的引號和分詞以及相關問題的痛苦(它們有自己的問題和怪癖) .
有兩個數組,
@first
並且@last
:#!/usr/bin/perl use strict; use DBI; my $dbfile= './people.db'; my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile"); my @first = qw(John Paul George Ringo); my @last = qw(Lennon McCartney Harrison Starr); $dbh->do('CREATE TABLE IF NOT EXISTS people(fname TEXT, lname TEXT)'); my $sth = $dbh->prepare('INSERT INTO people (fname, lname) VALUES (?,?)'); for my $i (0..$#first) { $sth->execute($first[$i],$last[$i]); };
這裡重要的是,因為我使用了準備好的語句,所以我不必關心任何名稱中的引號之類的煩人字元,也不必採取任何特殊措施來處理它們。準備好的語句會自動處理所有這些。
將其另存為,例如,
sql-arrays.pl
並使用chmod +x sql-arrays.pl
.您可以檢查它是否從 shell 正確插入了記錄:
$ sqlite3 people.db -cmd ".mode column" "SELECT * FROM people;" fname lname ------ --------- John Lennon Paul McCartney George Harrison Ringo Starr
當然,您可以編寫一個 perl 腳本來執行 SELECT 語句並以您喜歡的任何格式輸出記錄。我將把它作為練習留給讀者。
使用關聯數組(又名雜湊)
%people
,:#!/usr/bin/perl use strict; use DBI; my $dbfile= './people.db'; my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile"); my %people = ( 'John' => 'Lennon', 'Paul' => 'McCartney', 'George' => 'Harrison', 'Ringo' => 'Starr', ); $dbh->do("CREATE TABLE IF NOT EXISTS people(fname TEXT, lname TEXT);"); my $sth = $dbh->prepare('INSERT INTO people (fname, lname) VALUES (?,?)'); for my $person (keys %people) { $sth->execute($person,$people{$person}); };
注意:雜湊值沒有特定順序儲存,因此記錄將以半隨機順序插入到數據庫中。您可以使用
sort keys %people
而不是 justkeys %people
,但這將按排序順序(George,John,Paul,Ringo)插入記錄,這與提供的順序不同(John,Paul,George,Ringo)。大多數情況下,使用雜湊時,順序並不重要。如果是這樣,通常使用單獨的索引數組來儲存所需的順序,然後循環遍歷它而不是半隨機散列鍵。
例如
@order=qw(John Paul George Ringo);
,稍後,for my $person (@order) { ... };
而不是for my $person (keys %people) { ... }
.無論如何,您可以看到db中的記錄順序與第一個版本不同:
$ rm -f people.db $ ./sql-hash.pl $ sqlite3 people.db -cmd ".mode column" "SELECT * FROM people;" fname lname ------ --------- John Lennon Ringo Starr George Harrison Paul McCartney
還值得注意的是,這是一個非常人為的範例 - 雜湊鍵必須是唯一的,而名字距離那樣有很長的路要走。雖然這個例子很好地展示了樣本數據的基本思想,但在實際使用中,雜湊鍵將是 UUID 或非重複序列或其他一些唯一標識符,並且數據庫中的相應欄位將是首要的關鍵。
更重要的是,在執行 SELECT 查詢時,DBI 模組可以返回匹配行的一種方式是散列或散列引用,您可以遍歷鍵以輸出和/或處理數據。
順便說一句,
bash
也有關聯數組和索引數組。與 ksh、zsh 和 awk 一樣。大多數其他語言都有某種形式的關聯數組或元組或類似的。使用包含全名的字元串數組
@people
:#!/usr/bin/perl use strict; use DBI; my $dbfile= './people.db'; my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile"); my @people = ('John Lennon', 'Paul McCartney', 'George Harrison', 'Ringo Starr'); $dbh->do("CREATE TABLE IF NOT EXISTS people(fname TEXT, lname TEXT);"); my $sth = $dbh->prepare('INSERT INTO people (fname, lname) VALUES (?,?)'); foreach (@people) { my ($first,$last) = split; $sth->execute($first,$last); };
此版本將每個全名拆分為名字和姓氏,然後將它們插入數據庫。
如果您想從文本文件中讀取名稱列表(每行一個名稱)而不是使用硬編碼的字元串數組,則此表單特別有用。
上述範例腳本都無法處理中間名或頭銜如 Mr 或 Dr 的人,或者那些命名約定與大多數英語世界不同的人。通過足夠的努力,您可以強制他們更改名稱以適合您的程序,但僅更改算法(和數據庫結構)以處理此類現實世界的煩惱可能更容易。人們可以很不方便。尤其是流行歌星。