MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Bash中的脚本与数据库交互

2023-07-061.5k 阅读

数据库交互基础认知

在Bash脚本中与数据库交互,首先要理解不同类型数据库的特性以及常用交互方式。数据库从结构上大致分为关系型数据库(如MySQL、PostgreSQL)和非关系型数据库(如MongoDB、Redis)。关系型数据库基于表格结构存储数据,通过SQL语言进行数据操作;非关系型数据库则以不同的数据模型存储,如文档型(MongoDB)、键值对型(Redis)等,操作语言也各有不同。

与数据库交互的核心在于建立连接、执行查询或命令、处理返回结果。在Bash中,通常借助数据库提供的客户端工具或第三方库来实现这些操作。例如,MySQL数据库有官方的mysql客户端工具,PostgreSQL有psql工具,而对于一些非关系型数据库,也有相应的命令行工具或通过脚本语言(如Python与相应数据库驱动结合后,Bash通过调用Python脚本间接操作数据库)来交互。

关系型数据库交互 - MySQL示例

MySQL是最常用的关系型数据库之一。在Bash脚本中与MySQL交互,需确保系统已安装MySQL客户端。

安装MySQL客户端

在基于Debian或Ubuntu的系统上,可使用以下命令安装:

sudo apt update
sudo apt install mysql-client

在基于Red Hat或CentOS的系统上:

sudo yum install mysql - client

连接MySQL数据库

使用mysql命令连接数据库,基本语法为:

mysql -u用户名 -p密码 -h主机名 -P端口号 数据库名

例如,连接本地MySQL数据库,用户名是root,密码是password,数据库名为testdb:

mysql -uroot -ppassword -hlocalhost -P3306 testdb

在Bash脚本中,可将连接信息存储在变量中:

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"

mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE << EOF
# 在此处输入SQL语句
EOF

执行SQL查询

在连接成功后,可执行SQL查询。比如查询testdb数据库中users表的所有记录:

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"

RESULT=$(mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -N -e "SELECT * FROM users")
echo "$RESULT"

这里的-N选项表示不输出列名,-e选项用于直接执行SQL语句。执行结果存储在RESULT变量中并输出。

插入数据

向users表插入一条新记录:

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"

NAME="John"
AGE=30

mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -e "INSERT INTO users (name, age) VALUES ('$NAME', $AGE)"

注意,在使用变量值插入数据时,要确保数据类型正确且防止SQL注入。对于字符串类型,需用引号括起来。

处理查询结果

假设users表结构为(id INT, name VARCHAR(50), age INT),我们要对查询结果进行逐行处理:

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"

RESULT=$(mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -N -e "SELECT * FROM users")

while read -r id name age; do
    echo "ID: $id, Name: $name, Age: $age"
    # 在此处可对每一行数据进行更多处理,如计算、存储到文件等
done <<< "$RESULT"

这里通过while read -r循环逐行读取查询结果,并对每行的字段进行处理。

关系型数据库交互 - PostgreSQL示例

PostgreSQL也是一款强大的开源关系型数据库。

安装PostgreSQL客户端

在Debian或Ubuntu系统上:

sudo apt update
sudo apt install postgresql-client

在Red Hat或CentOS系统上:

sudo yum install postgresql - client

连接PostgreSQL数据库

使用psql命令连接,基本语法为:

psql -U用户名 -h主机名 -p端口号 数据库名

例如,连接本地PostgreSQL数据库,用户名是postgres,数据库名为mydb:

psql -U postgres -hlocalhost -p5432 mydb

在Bash脚本中:

#!/bin/bash
USER="postgres"
HOST="localhost"
PORT="5432"
DATABASE="mydb"

psql -U$USER -h$HOST -p$PORT $DATABASE << EOF
# 在此处输入SQL语句
EOF

执行SQL查询

查询mydb数据库中employees表的所有记录:

#!/bin/bash
USER="postgres"
HOST="localhost"
PORT="5432"
DATABASE="mydb"

RESULT=$(psql -U$USER -h$HOST -p$PORT $DATABASE -t -c "SELECT * FROM employees")
echo "$RESULT"

这里的-t选项表示只输出查询结果,不输出表头和一些额外信息,-c选项用于执行SQL命令。

插入数据

向employees表插入一条新记录:

#!/bin/bash
USER="postgres"
HOST="localhost"
PORT="5432"
DATABASE="mydb"

NAME="Jane"
AGE=25

psql -U$USER -h$HOST -p$PORT $DATABASE -c "INSERT INTO employees (name, age) VALUES ('$NAME', $AGE)"

同样,要注意数据类型和防止SQL注入。

处理查询结果

假设employees表结构为(id SERIAL, name VARCHAR(50), age INT),处理查询结果:

#!/bin/bash
USER="postgres"
HOST="localhost"
PORT="5432"
DATABASE="mydb"

RESULT=$(psql -U$USER -h$HOST -p$PORT $DATABASE -t -c "SELECT * FROM employees")

while IFS='|' read -r id name age; do
    echo "ID: $id, Name: $name, Age: $age"
    # 可对每行数据进一步处理
done <<< "$RESULT"

这里使用IFS='|'来指定字段分隔符,因为PostgreSQL默认的查询结果字段分隔符是|

非关系型数据库交互

非关系型数据库交互 - MongoDB示例

MongoDB是文档型非关系型数据库。

安装MongoDB客户端

在Debian或Ubuntu系统上:

wget -qO - https://www.mongodb.org/static/pgp/server - 4.4.asc | sudo apt - key add -
echo "deb [ arch = amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb - org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb - org - 4.4.list
sudo apt update
sudo apt install mongodb - org - shell

在Red Hat或CentOS系统上:

sudo tee /etc/yum.repos.d/mongodb - org - 4.4.repo << EOF
[mongodb - org - 4.4]
name = MongoDB Repository
baseurl = https://repo.mongodb.org/apt/rhel/8/mongodb - org/4.4/x86_64/
gpgcheck = 1
enabled = 1
gpgkey = https://www.mongodb.org/static/pgp/server - 4.4.asc
EOF
sudo yum install mongodb - org - shell

连接MongoDB数据库

使用mongo命令连接,基本语法为:

mongo "mongodb://主机名:端口号/数据库名" -u用户名 -p密码

例如,连接本地MongoDB数据库,数据库名为testdb,用户名是admin,密码是password:

mongo "mongodb://localhost:27017/testdb" -uadmin -ppassword

在Bash脚本中:

#!/bin/bash
HOST="localhost"
PORT="27017"
DATABASE="testdb"
USER="admin"
PASSWORD="password"

mongo "mongodb://$HOST:$PORT/$DATABASE" -u$USER -p$PASSWORD << EOF
# 在此处输入MongoDB命令
EOF

插入数据

向testdb数据库的users集合插入一条文档:

#!/bin/bash
HOST="localhost"
PORT="27017"
DATABASE="testdb"
USER="admin"
PASSWORD="password"

NAME="Bob"
AGE=28

mongo "mongodb://$HOST:$PORT/$DATABASE" -u$USER -p$PASSWORD << EOF
db.users.insertOne({name: '$NAME', age: $AGE})
EOF

这里使用insertOne方法插入单个文档。

查询数据

查询testdb数据库users集合中所有文档:

#!/bin/bash
HOST="localhost"
PORT="27017"
DATABASE="testdb"
USER="admin"
PASSWORD="password"

RESULT=$(mongo "mongodb://$HOST:$PORT/$DATABASE" -u$USER -p$PASSWORD --quiet --eval "db.users.find().toArray()")
echo "$RESULT"

--quiet选项减少输出冗余信息,--eval用于执行JavaScript表达式,这里执行查询并将结果转换为数组形式输出。

处理查询结果

由于MongoDB查询结果是JSON格式,在Bash中处理JSON需借助工具,如jq。假设已安装jq,处理查询结果:

#!/bin/bash
HOST="localhost"
PORT="27017"
DATABASE="testdb"
USER="admin"
PASSWORD="password"

RESULT=$(mongo "mongodb://$HOST:$PORT/$DATABASE" -u$USER -p$PASSWORD --quiet --eval "db.users.find().toArray()")

echo "$RESULT" | jq -r '.[] | "Name: \(.name), Age: \(.age)"'

这里jq-r选项使输出为原始字符串,.[]遍历数组中的每个文档,\(.name)\(.age)提取文档中的字段值。

非关系型数据库交互 - Redis示例

Redis是键值对型非关系型数据库,常用于缓存、消息队列等场景。

安装Redis客户端

在Debian或Ubuntu系统上:

sudo apt update
sudo apt install redis - tools

在Red Hat或CentOS系统上:

sudo yum install redis - tools

连接Redis数据库

使用redis - cli命令连接,基本语法为:

redis - cli -h主机名 -p端口号 -a密码

例如,连接本地Redis数据库,端口为6379,密码是password:

redis - cli -hlocalhost -p6379 -apassword

在Bash脚本中:

#!/bin/bash
HOST="localhost"
PORT="6379"
PASSWORD="password"

redis - cli -h$HOST -p$PORT -a$PASSWORD << EOF
# 在此处输入Redis命令
EOF

设置键值对

设置一个键为user:1,值为{"name":"Alice","age":35}的键值对:

#!/bin/bash
HOST="localhost"
PORT="6379"
PASSWORD="password"

VALUE='{"name":"Alice","age":35}'

redis - cli -h$HOST -p$PORT -a$PASSWORD set user:1 "$VALUE"

获取键值对

获取键user:1的值:

#!/bin/bash
HOST="localhost"
PORT="6379"
PASSWORD="password"

RESULT=$(redis - cli -h$HOST -p$PORT -a$PASSWORD get user:1)
echo "$RESULT"

处理复杂数据结构

Redis支持多种数据结构,如哈希、列表等。以哈希为例,设置一个用户哈希:

#!/bin/bash
HOST="localhost"
PORT="6379"
PASSWORD="password"

redis - cli -h$HOST -p$PORT -a$PASSWORD hset user:2 name "Eve" age 22

获取哈希中的所有字段和值:

#!/bin/bash
HOST="localhost"
PORT="6379"
PASSWORD="password"

RESULT=$(redis - cli -h$HOST -p$PORT -a$PASSWORD hgetall user:2)
echo "$RESULT"

处理哈希结果时,可通过脚本将其转换为更易读的格式:

#!/bin/bash
HOST="localhost"
PORT="6379"
PASSWORD="password"

RESULT=$(redis - cli -h$HOST -p$PORT -a$PASSWORD hgetall user:2)

while read -r key value; do
    echo "$key: $value"
done <<< "$RESULT"

数据库交互中的错误处理

在与数据库交互时,错误处理至关重要。不同数据库客户端工具返回的错误码和错误信息格式有所不同,但总体思路是相似的。

MySQL错误处理

在Bash脚本中执行MySQL命令时,可通过检查命令的返回状态码来判断是否执行成功。MySQL命令执行成功时返回状态码0,失败时返回非0值。

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"

mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -e "INSERT INTO users (name, age) VALUES ('Invalid Name', 'not a number')"
if [ $? -ne 0 ]; then
    echo "MySQL query failed. Check the error message."
    # 可进一步获取MySQL的详细错误信息,如通过stderr重定向
    mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -e "INSERT INTO users (name, age) VALUES ('Invalid Name', 'not a number')" 2>&1
fi

这里$?表示上一个命令的返回状态码,通过判断其是否为0来确定MySQL命令是否执行成功。若失败,可通过将stderr重定向到stdout(2>&1)来获取详细错误信息。

PostgreSQL错误处理

PostgreSQL的psql命令同样通过返回状态码表示执行结果。成功返回0,失败返回非0。

#!/bin/bash
USER="postgres"
HOST="localhost"
PORT="5432"
DATABASE="mydb"

psql -U$USER -h$HOST -p$PORT $DATABASE -c "INSERT INTO employees (name, age) VALUES ('Invalid', -1)"
if [ $? -ne 0 ]; then
    echo "PostgreSQL query failed. Check the error message."
    # 获取详细错误信息
    psql -U$USER -h$HOST -p$PORT $DATABASE -c "INSERT INTO employees (name, age) VALUES ('Invalid', -1)" 2>&1
fi

与MySQL类似,通过检查$?的值判断命令执行情况,并通过重定向stderr获取详细错误信息。

MongoDB错误处理

在Bash脚本中执行MongoDB命令时,可利用mongo命令的返回状态。成功执行JavaScript代码时返回0,失败返回非0。

#!/bin/bash
HOST="localhost"
PORT="27017"
DATABASE="testdb"
USER="admin"
PASSWORD="password"

mongo "mongodb://$HOST:$PORT/$DATABASE" -u$USER -p$PASSWORD << EOF
try {
    db.users.insertOne({name: 'Invalid', age: 'not a number'})
} catch (e) {
    print(e)
}
EOF
if [ $? -ne 0 ]; then
    echo "MongoDB operation failed. Check the error message above."
fi

这里在MongoDB的JavaScript代码块中使用try - catch捕获错误并打印,Bash脚本通过检查$?判断整体操作是否成功。

Redis错误处理

Redis - cli命令执行成功返回0,失败返回非0。

#!/bin/bash
HOST="localhost"
PORT="6379"
PASSWORD="password"

redis - cli -h$HOST -p$PORT -a$PASSWORD set user:3 "invalid value type"
if [ $? -ne 0 ]; then
    echo "Redis operation failed. Check the error message."
    # 获取详细错误信息
    redis - cli -h$HOST -p$PORT -a$PASSWORD set user:3 "invalid value type" 2>&1
fi

同样通过检查$?来判断Redis命令是否执行成功,并通过重定向stderr获取详细错误信息。

数据库交互的优化

在Bash脚本与数据库频繁交互时,优化操作可提高性能和效率。

批量操作

对于关系型数据库,如MySQL和PostgreSQL,尽量使用批量插入、更新等操作。例如,在MySQL中,将多条插入语句合并为一条:

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"

NAMES=("User1" "User2" "User3")
AGES=(20 22 25)

SQL="INSERT INTO users (name, age) VALUES "
for ((i = 0; i < ${#NAMES[@]}; i++)); do
    SQL+="('${NAMES[$i]}', ${AGES[$i]})"
    if [ $i -lt $((${#NAMES[@]} - 1)) ]; then
        SQL+=","
    fi
done

mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -e "$SQL"

这样相比多次执行单个插入语句,可减少数据库连接开销,提高插入效率。

对于MongoDB,可使用insertMany方法进行批量插入:

#!/bin/bash
HOST="localhost"
PORT="27017"
DATABASE="testdb"
USER="admin"
PASSWORD="password"

DOCUMENTS='[
    {name: "Doc1", age: 20},
    {name: "Doc2", age: 22},
    {name: "Doc3", age: 25}
]'

mongo "mongodb://$HOST:$PORT/$DATABASE" -u$USER -p$PASSWORD << EOF
db.users.insertMany($DOCUMENTS)
EOF

缓存查询结果

在一些场景下,部分查询结果变化频率较低,可将其缓存起来。对于Redis,可将数据库查询结果缓存为键值对。例如,在Bash脚本中查询MySQL数据库中某个经常使用的统计信息,并缓存到Redis:

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"
REDIS_HOST="localhost"
REDIS_PORT="6379"
REDIS_PASSWORD="password"

CACHE_KEY="user_count"
RESULT=$(redis - cli -h$REDIS_HOST -p$REDIS_PORT -a$REDIS_PASSWORD get $CACHE_KEY)
if [ -z "$RESULT" ]; then
    RESULT=$(mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -N -e "SELECT COUNT(*) FROM users")
    redis - cli -h$REDIS_HOST -p$REDIS_PORT -a$REDIS_PASSWORD set $CACHE_KEY "$RESULT"
fi
echo "User count: $RESULT"

这里先检查Redis缓存中是否有用户数量的结果,若没有则查询MySQL数据库并将结果缓存到Redis。

合理使用索引

在关系型数据库中,索引对查询性能影响巨大。确保在经常用于查询条件的字段上创建索引。例如,在MySQL的users表中,若经常按name字段查询:

#!/bin/bash
USER="root"
PASSWORD="password"
HOST="localhost"
PORT="3306"
DATABASE="testdb"

mysql -u$USER -p$PASSWORD -h$HOST -P$PORT $DATABASE -e "CREATE INDEX idx_name ON users (name)"

对于MongoDB,同样可在集合字段上创建索引来优化查询:

#!/bin/bash
HOST="localhost"
PORT="27017"
DATABASE="testdb"
USER="admin"
PASSWORD="password"

mongo "mongodb://$HOST:$PORT/$DATABASE" -u$USER -p$PASSWORD << EOF
db.users.createIndex({name: 1})
EOF

通过合理使用索引,可大幅提高查询效率,减少与数据库交互的时间。

在Bash脚本与数据库交互的实际应用中,需根据具体业务场景、数据库特性等因素,综合运用上述技术和优化方法,以实现高效、稳定的数据库交互。同时,要注意安全问题,如数据库用户权限管理、防止SQL注入等,确保数据库和数据的安全性。