Bash

這些 bash 字元串中的單詞可以有效地插入到 sqlite 表中嗎?

  • May 8, 2021

我有兩個 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 中執行此操作的範例:

所有這些範例都需要安裝DBIDBD::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而不是 just keys %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 的人,或者那些命名約定與大多數英語世界不同的人。通過足夠的努力,您可以強制他們更改名稱以適合您的程序,但僅更改算法(和數據庫結構)以處理此類現實世界的煩惱可能更容易。人們可以很不方便。尤其是流行歌星。

引用自:https://unix.stackexchange.com/questions/648286