DMSHX (D(M) + (S)SH + (H)ost e(X)ecutor) 是一个跨平台、零依赖的命令行工具,用于远程主机批量命令执行、配置文件处理、达梦数据库SQL查询,并统一输出为JSON,适合运维集成或Python自动化调用。
- 支持用户名+密码/私钥认证
- 支持per-host SSH端口配置(例如10.0.0.1:2222)
- 支持传参方式执行任意shell命令(如cat, sed, echo)
- 支持命令超时控制(单位秒)
- 支持SFTP文件上传到远程主机
- 支持自动创建远程目录结构
- 支持设置上传文件的权限
- 支持上传超时控制
- 支持多主机并行上传
- 支持从远程主机下载单个文件或整个目录
- 支持MD5校验确保文件完整性
- 提供实时进度显示,包括下载速度、剩余时间等
- 支持多主机并行下载
- 支持下载超时控制
- 支持数据库类型:
- 达梦数据库(DM)
- Oracle(计划支持)
- 支持SQL查询语句,返回结构封装为JSON
- 支持SQL执行超时设置
- 所有命令或SQL执行统一返回结构,便于机器解析与日志归档
- 记录每条命令的接收时间和执行结果
- 支持日志定期自动清理
- 可配置日志保留天数
- 按日期自动组织日志文件,便于查找和管理
- 支持UTF-8编码,确保中文正确显示
从Releases页面下载适合您平台的可执行文件:
- Windows x86_64:
dmshx-windows-x86_64.exe - Linux x86_64:
dmshx-linux-x86_64 - Linux ARM64:
dmshx-linux-arm64
需要Go 1.19或更高版本。
git clone https://github.com/gaoyuan98/dmshx/dmshx.git
cd dmshx
go build -o dmshx ./cmd/dmshx在Windows环境下,可以使用提供的构建脚本一键编译多平台版本:
# 或者使用批处理文件
.\build_dmshx.bat默认编译的Go程序通常较大,这是因为它们是静态链接的。build_dmshx.bat脚本已包含以下优化:
- 使用
-ldflags "-s -w"移除调试信息和符号表 - 使用
-trimpath移除编译路径信息 - 支持UPX工具进行可执行文件压缩(需单独安装)
如果需要进一步减小文件大小,可以手动安装 UPX 工具:
- 从 UPX官网 下载适合您系统的版本
- 将 upx 可执行文件放入系统 PATH 目录或与构建脚本相同目录
- 重新运行构建脚本
通过这些优化,可执行文件通常可以减小 50%-70%。
注意:命令行参数使用Go标准flag包格式,使用单横线(
-)作为参数前缀。 参数语法规范:为保持一致性,所有参数统一使用-flag=value格式(布尔值可以使用-flag表示true)。
# 使用密码在单台主机上执行命令
dmshx -host="192.168.112.168" -port=22 -user="root" -password="gaoyuan123#" -cmd="cat /opt/dmdata/5236/DMDB/dm.ini"
# 使用私钥在多台主机上执行命令
dmshx -hosts="192.168.1.10,192.168.1.11:2222" -user="root" -key="/path/to/id_rsa" -cmd="cat /etc/hosts" -timeout=30
# 从文件读取主机列表
dmshx -host-file="hosts.txt" -user="admin" -password="password" -cmd="uptime"
# 以root用户连接,但以dmdba用户执行命令
dmshx -hosts="192.168.1.10,192.168.1.11" -user="root" -password="rootpassword" -cmd="cat /opt/dmdata/5236/DMDB/dm.ini" -exec-user="dmdba"# 达梦数据库查询
dmshx -db-type="dm" -db-host="192.168.112.168" -db-port=5236 -db-user="SYSDBA" -db-pass="Dameng123#" -sql="SELECT * FROM V$INSTANCE" -timeout=60
# 设置查询超时
dmshx -db-type="dm" -db-host="192.168.1.20" -db-user="SYSDBA" -db-pass="SYSDBA" -sql="SELECT * FROM LARGE_TABLE" -timeout=60
# 执行不限制超时的查询(适用于大型报表查询)
dmshx -db-type="dm" -db-host="192.168.112.168" -db-port=5236 -db-user="SYSDBA" -db-pass="Dameng123#" -sql="SELECT * FROM LARGE_TABLE JOIN ANOTHER_TABLE" -timeout=0
# 执行非查询语句
dmshx -db-type="dm" -db-host="192.168.112.168" -db-port=5236 -db-user="SYSDBA" -db-pass="Dameng123#" -sql="INSERT INTO SYSDBA.T_EMP(ID,NAME) VALUES(1001,'张三')" -sql-type=INS# 输出为JSON格式(默认)
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -json-output=true
# 输出为文本格式
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -json-output=false
# 输出到日志文件
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -log-file="output.log"
# 关闭UTF-8编码(适用于特殊终端环境)
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -enable-utf8=false
# 启用命令执行日志记录并设置保留天数
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -enable-command-log=true -log-retention=30
# 启用实时输出(仅在非JSON模式下有效)
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -json-output=false -real-time=true
# 显示版本信息
dmshx -version| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| -hosts | string | "" | 多主机逗号分隔列表,支持格式 ip[:port],例如 "192.168.1.10,192.168.1.11:2222" |
| -host | string | "" | 单主机设置,支持格式 ip[:port],与-hosts功能相同但只接受单个主机 |
| -host-file | string | "" | 主机列表文件路径,文件中每行包含一个主机,格式为 ip[:port] |
| -port | int | 22 | 默认SSH连接端口(全局设置),在hosts未指定端口时使用 |
| -user | string | "" | SSH登录用户名,用于远程主机认证 |
| -key | string | "" | SSH私钥文件路径,优先级高于密码认证 |
| -password | string | "" | SSH登录密码,仅在未提供私钥时使用(不推荐在生产环境直接使用) |
| -cmd | string | "" | 在远程主机执行的Shell命令,例如 "ls -la /opt" 或 "cat /etc/hosts" |
| -timeout | int | 30 | 命令或SQL执行超时时间,单位为秒,超时后会终止执行 |
| -exec-user | string | "" | 执行命令的用户,如果设置且与SSH登录用户不同,将使用su切换到该用户执行命令 |
| -upload-file | string | "" | 要上传到远程主机的本地文件路径 |
| -upload-dir | string | "" | 远程主机上的目标目录,文件将上传到此目录下 |
| -upload-perm | int | 0644 | 上传文件的权限设置(八进制),默认为0644 |
| -remote-path | string | "" | 要从远程主机下载的文件或目录路径 |
| -local-path | string | "" | 下载文件保存到本地的目录路径 |
| -verify-md5 | bool | true | 是否验证下载文件的MD5校验和,确保文件完整性 |
| -buffer-size | int64 | 32 | 下载文件时使用的缓冲区大小,单位为MB |
| -db-type | string | "" | 数据库类型,当前支持 "dm"(达梦数据库),未来计划支持 "oracle" |
| -db-host | string | "" | 数据库服务器主机名或IP地址 |
| -db-port | int | 0 | 数据库服务端口,达梦数据库默认为5236 |
| -db-user | string | "" | 数据库连接用户名 |
| -db-pass | string | "" | 数据库连接密码 |
| -db-name | string | "" | 数据库名称或SID(Oracle) |
| -sql | string | "" | 要执行的SQL查询语句,例如 "SELECT * FROM V$INSTANCE" |
| -sql-type | string | "SEL" | SQL语句类型,默认SEL(查询);设置为INS时执行非查询语句(INSERT/UPDATE/DELETE/DDL) |
| -json-output | bool | true | 是否以JSON格式输出结果,便于程序解析,默认开启 |
| -log-file | string | "" | 执行结果输出日志文件路径,若指定则同时输出到屏幕和文件 |
| -version, -v | bool | false | 显示程序版本号、构建时间、作者和构建日期信息 |
| -real-time | bool | false | 启用命令执行实时输出功能,只在非JSON输出模式下有效(-json-output=false) |
| -enable-utf8 | bool | true | 启用UTF-8编码输出,在Windows环境下自动设置控制台代码页为65001(UTF-8),确保中文正确显示 |
| -enable-command-log | bool | true | 是否启用命令执行日志记录功能,默认开启 |
| -command-log-path | string | "./logs" | 命令执行日志存储目录 |
| -log-retention | int | 7 | 日志文件保留天数,同时也是清理检查的间隔天数 |
dmshx支持两种输出格式:JSON格式(默认)和文本格式。所有输出都包含统一的字段结构,便于程序解析和日志归档。
成功执行示例:
{
"host": "192.168.112.168",
"type": "cmd",
"status": "success",
"stdout": "total 8\ndrwxr-xr-x 2 root root 4096 Jun 17 08:45 .\ndrwxr-xr-x 3 root root 4096 Jun 17 08:44 ..\n-rw-r--r-- 1 root root 123 Jun 17 08:45 test.txt",
"stderr": "",
"duration": "2.45s",
"timestamp": "2025-06-17 08:45:12",
"error": "",
"ssh_user": "root",
"exec_user": "dmdba",
"actual_cmd": "su - dmdba -c 'ls -la'",
"timeout_setting": "30秒"
}执行失败示例:
{
"host": "192.168.112.168",
"type": "cmd",
"status": "error",
"stdout": "",
"stderr": "ls: cannot access '/nonexistent': No such file or directory",
"duration": "0.12s",
"timestamp": "2025-06-17 08:45:12",
"error": "exit status 2"
}连接失败示例:
{
"host": "192.168.112.168",
"type": "cmd",
"status": "error",
"stdout": "",
"stderr": "",
"duration": "0s",
"timestamp": "2025-06-17 08:45:12",
"error": "dial tcp 192.168.112.168:22: connect: connection refused"
}成功查询示例:
{
"host": "192.168.112.168",
"type": "sql",
"db": "dm",
"status": "success",
"rows": [
{
"INSTANCE_NAME": "DAMENG",
"VERSION": "8.0.0.128",
"STATUS": "ACTIVE",
"STARTUP_TIME": "2025-06-17 08:00:00"
},
{
"INSTANCE_NAME": "DAMENG2",
"VERSION": "8.0.0.128",
"STATUS": "ACTIVE",
"STARTUP_TIME": "2025-06-17 08:30:00"
}
],
"duration": "0.91s",
"timestamp": "2025-06-17 08:45:12",
"error": "",
"timeout_setting": "30秒"
}查询失败示例:
{
"host": "192.168.112.168",
"type": "sql",
"db": "dm",
"status": "error",
"rows": [],
"duration": "0.05s",
"timestamp": "2025-06-17 08:45:12",
"error": "table or view does not exist: NONEXISTENT_TABLE"
}连接失败示例:
{
"host": "192.168.112.168",
"type": "sql",
"db": "dm",
"status": "error",
"rows": [],
"duration": "0s",
"timestamp": "2025-06-17 08:45:12",
"error": "dial tcp 192.168.112.168:5236: connect: connection refused"
}上传成功示例:
{
"host": "192.168.1.10",
"type": "upload",
"status": "success",
"local_file": "/path/to/localfile.txt",
"remote_file": "/opt/destination/localfile.txt",
"size": 12345,
"duration": "1.23s",
"timestamp": "2025-06-17 08:45:12",
"ssh_user": "root",
"timeout_setting": "30秒"
}上传失败示例:
{
"host": "192.168.1.10",
"type": "upload",
"status": "error",
"local_file": "/path/to/localfile.txt",
"remote_file": "/opt/destination/localfile.txt",
"size": 0,
"duration": "0.05s",
"timestamp": "2025-06-17 08:45:12",
"ssh_user": "root",
"error": "创建远程目录失败: permission denied"
}下载成功示例:
{
"host": "192.168.1.10",
"type": "download",
"status": "success",
"remote_path": "/opt/source/file.txt",
"local_path": "/downloads/file.txt",
"size": 12345,
"md5": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"duration": "1.23s",
"timestamp": "2025-06-17 08:45:12",
"ssh_user": "root",
"timeout_setting": "30秒"
}下载失败示例:
{
"host": "192.168.1.10",
"type": "download",
"status": "error",
"remote_path": "/opt/source/file.txt",
"local_path": "/downloads/file.txt",
"size": 0,
"duration": "0.05s",
"timestamp": "2025-06-17 08:45:12",
"ssh_user": "root",
"error": "远程文件不存在或无法访问: no such file or directory"
}当设置 -json-output=false 时,输出为易读的文本格式:
成功执行:
Host: 192.168.112.168
Type: cmd
Status: success
Timestamp: 2025-06-17 08:45:12
Stdout: total 8
drwxr-xr-x 2 root root 4096 Jun 17 08:45 .
drwxr-xr-x 3 root root 4096 Jun 17 08:44 ..
-rw-r--r-- 1 root root 123 Jun 17 08:45 test.txt
Stderr:
Duration: 2.45s
执行失败:
Host: 192.168.112.168
Type: cmd
Status: error
Timestamp: 2025-06-17 08:45:12
Stdout:
Stderr: ls: cannot access '/nonexistent': No such file or directory
Duration: 0.12s
Error: exit status 2
成功查询:
Host: 192.168.112.168
Type: sql
DB: dm
Status: success
Timestamp: 2025-06-17 08:45:12
Duration: 0.91s
Rows: 2
Row 1: map[INSTANCE_NAME:DAMENG VERSION:8.0.0.128 STATUS:ACTIVE]
Row 2: map[INSTANCE_NAME:DAMENG2 VERSION:8.0.0.128 STATUS:ACTIVE]
查询失败:
Host: 192.168.112.168
Type: sql
DB: dm
Status: error
Timestamp: 2025-06-17 08:45:12
Duration: 0.05s
Rows: 0
Error: table or view does not exist: NONEXISTENT_TABLE
| 字段名 | 类型 | 说明 |
|---|---|---|
host |
string | 目标主机IP地址或主机名 |
type |
string | 执行类型,SSH命令为"cmd",SQL查询为"sql" |
status |
string | 执行状态,"success"表示成功,"error"表示失败 |
duration |
string | 执行耗时,格式为"Xs"(如"2.45s") |
timestamp |
string | 执行完成时间戳,格式为"YYYY-MM-DD HH:MM:SS" |
ssh_user |
string | SSH连接使用的用户名 |
exec_user |
string | 实际执行命令的用户名,当使用-exec-user参数时会与ssh_user不同 |
actual_cmd |
string | 实际执行的命令字符串,当使用-exec-user参数时会与原始命令不同 |
timeout_setting |
string | 执行命令的超时设置,如"30秒"或"无限制" |
| 字段名 | 类型 | 说明 |
|---|---|---|
stdout |
string | 命令的标准输出内容 |
stderr |
string | 命令的标准错误输出内容 |
error |
string | 执行过程中的错误信息(仅在失败时存在) |
| 字段名 | 类型 | 说明 |
|---|---|---|
db |
string | 数据库类型,如"dm"、"oracle" |
rows |
array | 查询结果行数组,每行为一个对象,键为列名,值为列值 |
error |
string | 查询过程中的错误信息(仅在失败时存在) |
timeout_setting |
string | 执行SQL查询的超时设置,如"30秒"或"无限制" |
当指定多个主机时,每个主机的执行结果会分别输出:
# 执行命令
dmshx -hosts "192.168.1.10,192.168.1.11" -user "root" -password "password" -cmd "uptime"输出示例:
{
"host": "192.168.1.10",
"type": "cmd",
"status": "success",
"stdout": " 08:45:12 up 5 days, 12:30, 1 user, load average: 0.52, 0.48, 0.45",
"stderr": "",
"duration": "1.23s",
"timestamp": "2025-06-17 08:45:12"
}
{
"host": "192.168.1.11",
"type": "cmd",
"status": "success",
"stdout": " 08:45:13 up 3 days, 8:15, 2 users, load average: 0.78, 0.65, 0.52",
"stderr": "",
"duration": "1.45s",
"timestamp": "2025-06-17 08:45:13"
}dmshx对不同类型的错误提供详细的错误信息:
- 连接错误:网络连接失败、认证失败等
- 执行错误:命令执行失败、SQL语法错误等
- 超时错误:执行时间超过设定的超时时间
- 权限错误:SSH认证失败、数据库权限不足等
所有错误都会在error字段中提供具体的错误描述,便于问题诊断和调试。
启用命令执行日志记录功能后,系统将自动为每条命令创建日志文件,格式如下:
日志文件位置: {command-log-path}/{yyyy-MM-dd}/command_{timestamp}.log
日志内容格式:
执行时间: 2023-06-17 08:45:12
命令类型: SSH
目标主机: 192.168.1.10
SSH用户: root
执行用户: dmdba (仅当与SSH用户不同时显示)
原始命令: ls -la
实际命令: su - dmdba -c 'ls -la' (仅当与原始命令不同时显示)
超时设置: 30秒
执行状态: 成功/失败
执行耗时: 2.45s
执行结果: ...
系统会在每次启动时以及每24小时自动检查并清理过期日志文件(超过设定的保留天数)。清理时会按照目录日期进行判断,自动删除整个过期日期的目录。
以下是一些常用配置场景的示例:
- 批量执行命令并记录日志:
dmshx -hosts "server1,server2,server3" -user "admin" -key "/path/to/id_rsa" -cmd "uptime" -enable-command-log -log-retention 30- 执行达梦数据库查询并输出到文件:
dmshx -db-type "dm" -db-host "192.168.112.168" -db-port 5236 -db-user "SYSDBA" -db-pass "Dameng123#" -sql "SELECT * FROM V$VERSION" -log-file "dm_version.json"- 读取主机列表文件并执行命令:
dmshx -host-file "production_servers.txt" -user "ops" -password "secure_pass" -cmd "systemctl status nginx" -timeout 60- 以root用户连接但以dmdba用户执行命令:
dmshx -hosts "192.168.1.10,192.168.1.11" -user "root" -password "rootpassword" -cmd "cat /opt/dmdata/5236/DMDB/dm.ini" -exec-user "dmdba"
dmshx -hosts "192.168.1.10,192.168.1.11" -user "root" -password "rootpassword" -cmd "/opt/dmdbms/bin/DmServiceDM01 restart" -exec-user "dmdba"
这种方式实际执行的命令是:su - dmdba -c 'ps -ef | grep dms',适用于需要以特定用户身份执行命令的场景,例如操作达梦数据库时需要使用dmdba用户权限。
- 设置超时时间为0(不限制超时):
dmshx -hosts "192.168.112.168" -user "root" -password "gaoyuan123#" -cmd "tar -czf backup.tar.gz /opt/dmdata" -timeout 0这种设置适用于执行时间不可预测的长时间运行命令,如备份、大文件传输等。
dmshx -hosts "192.168.1.10" -user "root" -password "password" -cmd "ls -la" -log-file "output.log"
dmshx -hosts "192.168.112.168" -user "root" -password "gaoyuan123#" -cmd "/opt/dmdbms/bin/DmServiceDM01 restart" -json-output=false -real-time
dmshx -hosts "192.168.1.10" -user "root" -password "password" -cmd "ls -la" -enable-command-log -log-retention 30
### 实时输出模式
dmshx支持实时输出模式,可在执行耗时较长的命令时提供更好的用户体验:
```bash
# 启用实时输出模式(需要设置json-output=false)
dmshx -hosts "192.168.112.168" -user "root" -password "gaoyuan123#" -cmd "/opt/dmdbms/bin/DmServiceDM01 restart" -json-output=false -real-time
在实时输出模式下:
- 命令开始执行时显示提示信息
- 命令执行过程中的输出会实时显示在终端
- 命令完成后显示执行结果摘要
- 最终以文本格式输出完整结果
此模式特别适合执行耗时较长的操作(如数据库启停、备份还原等),使用户可以实时查看执行进度。
注意: 实时输出模式只在-json-output=false时有效,因为JSON格式必须作为完整结构输出。
# 上传单个文件到远程主机
dmshx -hosts="192.168.1.10" -user="root" -password="password" -upload-file="/path/to/local/file.txt" -upload-dir="/remote/directory"
# 设置上传文件权限
dmshx -hosts="192.168.1.10" -user="root" -password="password" -upload-file="/path/to/local/file.txt" -upload-dir="/remote/directory" -upload-perm=0755# 下载远程文件到本地
dmshx -hosts="192.168.1.10" -user="root" -password="password" -remote-path="/path/to/remote/file.txt" -local-path="/local/directory"
# 下载整个目录,禁用MD5验证
dmshx -hosts="192.168.1.10" -user="root" -password="password" -remote-path="/path/to/remote/directory" -local-path="/local/directory" -verify-md5=false
# 设置更大的下载缓冲区
dmshx -hosts="192.168.1.10" -user="root" -password="password" -remote-path="/path/to/remote/file.txt" -local-path="/local/directory" -buffer-size=64# 输出为JSON格式(默认)
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -json-output=true
# 输出为文本格式
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -json-output=false
# 输出到日志文件
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -log-file="output.log"
# 关闭UTF-8编码(适用于特殊终端环境)
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -enable-utf8=false
# 启用命令执行日志记录并设置保留天数
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -enable-command-log=true -log-retention=30
# 启用实时输出(仅在非JSON模式下有效)
dmshx -hosts="192.168.1.10" -user="root" -password="password" -cmd="ls -la" -json-output=false -real-time=true
# 显示版本信息
dmshx -version