豆包AI写的perl脚本程序:解析包含多个sql语句的sql文件,输出(并执行)之;支持复杂的注释、字符串内包含分隔符注释符、并支持自定义delimiter
```perl
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
# 全局变量,用于控制脚本的执行模式
# 当 $run_at_server 为 1 时,脚本会连接到 MySQL 数据库并执行 SQL 语句
# 当 $run_at_server 为 0 时,脚本仅打印 SQL 语句,不连接数据库也不执行
my $run_at_server = 0;
# 检查命令行参数,确保用户提供了 SQL 文件的路径
# 如果未提供,程序将终止并输出使用说明
if (@ARGV != 1) {
die "Usage: $0 <SQL file path>\n";
}
# 获取用户提供的 SQL 文件路径
my $file_path = $ARGV[0];
# 定义数据库连接信息,使用 DSN(数据源名称)格式
# database=mysql 表示连接到名为 mysql 的数据库
# host=localhost 表示连接本地的 MySQL 服务器
# mysql_ssl=0 表示不使用 SSL 连接
# mysql_enable_utf8=1 表示启用 UTF - 8 字符编码
my $dsn = "DBI:mysql:database=mysql;host=localhost;mysql_ssl=0;mysql_enable_utf8=1";
# 定义连接数据库的用户名
my $user = 'root';
# 定义连接数据库的密码
my $password = 'Netpart123';
# 声明数据库连接句柄变量,用于后续连接数据库操作
my $dbh;
# 初始分隔符设置为分号,用于识别 SQL 语句的结束位置
my $delimiter = ';';
# 用于存储当前正在处理的 SQL 语句
my $sql_statement = '';
# 标记是否处于字符串内
# 1 表示处于字符串内,此时遇到的分隔符和注释符将作为字符串的一部分
# 0 表示不在字符串内
my $in_string = 0;
# 记录当前字符串使用的引号类型(单引号或双引号)
# 当进入字符串时,会记录当前引号类型,在退出字符串时进行匹配检查
my $quote_char;
# 标记是否处于块注释内
# 1 表示处于块注释(/* ... */)内,此时忽略块内的所有内容
# 0 表示不在块注释内
my $in_block_comment = 0;
# 打开用户指定的 SQL 文件,以只读模式读取
# 如果文件打开失败,程序将终止并输出错误信息
open(my $file_handle, '<', $file_path) or die "Can't open file $file_path: $!";
# 逐行读取 SQL 文件
while (my $line = <$file_handle>) {
# 检查当前行是否为 DELIMITER 语句(不区分大小写)
# 如果是,更新分隔符为新指定的值,并跳过当前行继续处理下一行
if ($line =~ /^\s*DELIMITER\s+(\S+)\s*$/i) {
$delimiter = $1;
next;
}
# 获取当前行的字符长度
my $length = length($line);
# 遍历当前行的每个字符
for (my $i = 0; $i < $length; $i++) {
# 获取当前位置的字符
my $char = substr($line, $i, 1);
# 如果当前处于字符串内
if ($in_string) {
# 将当前字符添加到正在处理的 SQL 语句中
$sql_statement .= $char;
# 检查当前字符是否为字符串开始时记录的引号,并且该引号前不是转义字符
# 如果是,则表示字符串结束,将 $in_string 标记为 0
if ($char eq $quote_char && ($i == 0 || substr($line, $i - 1, 1) ne '\\')) {
$in_string = 0;
}
# 跳过本次循环的剩余部分,继续处理下一个字符
next;
}
# 如果当前处于块注释内
if ($in_block_comment) {
# 检查当前字符是否为 '*',且下一个字符为 '/'
# 如果是,则表示块注释结束,将 $in_block_comment 标记为 0
# 同时跳过下一个字符(即 '/')
if ($char eq '*' && $i + 1 < $length && substr($line, $i + 1, 1) eq '/') {
$in_block_comment = 0;
$i++;
}
# 跳过本次循环的剩余部分,继续处理下一个字符
next;
}
# 检查当前字符是否为单引号或双引号
if ($char eq "'" || $char eq '"') {
# 表示进入字符串,将 $in_string 标记为 1
$in_string = 1;
# 记录当前字符串使用的引号类型
$quote_char = $char;
# 将当前字符添加到正在处理的 SQL 语句中
$sql_statement .= $char;
# 跳过本次循环的剩余部分,继续处理下一个字符
next;
}
# 检查当前字符是否为 '/',且下一个字符为 '*'
if ($char eq '/' && $i + 1 < $length && substr($line, $i + 1, 1) eq '*') {
# 表示进入块注释,将 $in_block_comment 标记为 1
$in_block_comment = 1;
# 跳过下一个字符(即 '*')
$i++;
# 跳过本次循环的剩余部分,继续处理下一个字符
next;
}
# 检查当前字符是否为 '-' 且下一个字符也为 '-',或者当前字符为 '#'
if (($char eq '-' && $i + 1 < $length && substr($line, $i + 1, 1) eq '-') || $char eq '#') {
# 表示遇到行注释,忽略当前行的剩余部分,跳出当前循环
last;
}
# 将当前字符添加到正在处理的 SQL 语句中
$sql_statement .= $char;
# 检查当前 SQL 语句是否以分隔符结尾(后面可能跟有空白字符)
if ($sql_statement =~ /\Q$delimiter\E\s*$/) {
# 去除 SQL 语句末尾的分隔符和可能的空白字符
$sql_statement =~ s/\Q$delimiter\E\s*$//;
# 去除 SQL 语句前后的空白字符
$sql_statement =~ s/^\s+|\s+$//g;
# 检查处理后的 SQL 语句是否不为空(排除仅含空格或空行的情况)
if ($sql_statement && $sql_statement !~ /^\s*$/) {
# 打印即将执行的 SQL 语句
print "About to execute SQL: $sql_statement\n";
# 根据 $run_at_server 的值决定是否执行 SQL 语句
if ($run_at_server) {
# 如果数据库连接句柄未定义,则进行数据库连接
if (!defined $dbh) {
$dbh = DBI->connect($dsn, $user, $password, {
RaiseError => 1,
PrintError => 0
}) or die "Could not connect to database: " . $DBI::errstr;
}
# 使用 eval 块捕获可能的异常
eval {
# 检查 SQL 语句是否为 SELECT 语句(不区分大小写)
if ($sql_statement =~ /^\s*SELECT\s+/i) {
# 准备 SQL 语句
my $sth = $dbh->prepare($sql_statement);
# 执行 SQL 语句
$sth->execute();
# 获取查询结果的字段名
my $fields = $sth->{NAME};
# 处理字段名可能存在的未初始化值,将其设置为空字符串
for my $field (@$fields) {
$field = '' if !defined $field;
}
# 打印字段名,用制表符分隔
print join("\t", @$fields), "\n";
# 逐行获取查询结果
while (my $row = $sth->fetchrow_arrayref) {
# 处理行数据可能存在的未初始化值,将其设置为空字符串
for my $value (@$row) {
$value = '' if !defined $value;
}
# 打印每行数据,用制表符分隔
print join("\t", @$row), "\n";
}
# 关闭查询句柄
$sth->finish();
} else {
# 执行非 SELECT 类型的 SQL 语句
$dbh->do($sql_statement);
}
# 打印 SQL 语句执行成功的信息
print "Executed successfully: $sql_statement\n";
};
# 如果执行过程中出现异常
if ($@) {
# 打印执行失败的 SQL 语句和错误信息
print "Error executing statement: $sql_statement\nError: $@\n";
}
}
}
# 重置 SQL 语句变量,准备处理下一个 SQL 语句
$sql_statement = '';
}
}
}
# 如果 $run_at_server 为 1 且数据库连接已建立($dbh 已定义),则关闭数据库连接
if ($run_at_server && defined $dbh) {
$dbh->disconnect;
}
# 关闭 SQL 文件
close($file_handle);
```
### 部署说明
#### 1. 环境准备
- **Perl 环境**:确保系统中已经安装了 Perl 解释器。大多数 Linux 发行版和 macOS 系统默认已经安装了 Perl,你可以通过以下命令检查版本:
```bash
perl -v
```
如果未安装,可根据系统的包管理工具进行安装,例如在 Ubuntu 上:
```bash
sudo apt-get install perl
```
在 CentOS 上:
```bash
sudo yum install perl
```
- **DBI 和 DBD::mysql 模块**:脚本使用了 `DBI` 和 `DBD::mysql` 模块来连接和操作 MySQL 数据库,需要确保这两个模块已经安装。可以使用 CPAN 来安装:
```bash
cpan DBI DBD::mysql
```
安装过程中可能需要输入系统管理员密码进行权限确认。
#### 2. 脚本配置
- **数据库连接信息**:打开脚本文件,根据实际情况修改数据库连接信息,包括 `$dsn`、`$user` 和 `$password`。例如,如果数据库不在本地或者数据库名不是 `mysql`,需要相应调整 `$dsn` 的值。
- **执行模式**:根据需要修改全局变量 `$run_at_server` 的值。如果希望仅打印 SQL 语句而不连接数据库执行,将其设置为 `0`;如果要连接数据库并执行 SQL 语句,将其设置为 `1`。
#### 3. 运行脚本
将脚本保存为一个 `.pl` 文件(例如 `sql_executor.pl`),并确保该文件具有可执行权限:
```bash
chmod +x sql_executor.pl
```
然后在命令行中运行脚本,同时指定要处理的 SQL 文件路径:
```bash
./sql_executor.pl /path/to/your/sql/file.sql
```
请将 `/path/to/your/sql/file.sql` 替换为实际的 SQL 文件路径。脚本将根据 `$run_at_server` 的值决定是仅打印 SQL 语句还是连接数据库并执行这些语句。
#### 4. 错误处理
- 如果在运行脚本过程中遇到数据库连接错误,检查数据库服务是否正常运行,以及数据库连接信息是否正确。
- 如果遇到 SQL 语句执行错误,脚本会打印出具体的错误信息,可以根据错误信息对 SQL 语句进行调整。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤