1. 查看一次服务器BMC功率的脚本代码
利用shell脚本查看服务器的BMC的功率首先要下载ipmitool,在Ubuntu/Debian上下载ipmitool相关代码的指令为:
bash
sudo apt update
sudo apt install -y ipmitool
在CentOS 7下载ipmitool相关指令为:
bash
sudo yum install -y ipmitool
创建一个获得BMC的脚本并编写:
bash
vim Get_BMC.sh
脚本内容(使用AI生成)为:
bash
#!/usr/bin/env bash
# Get_BMC.sh
# 通过 BMC/IPMI 获取服务器整机瞬时功耗并打印
#
# 固定目标 BMC IP:××.××.××.××
# 固定用户名:××××××
# 固定密码:××××××
set -euo pipefail
BMC_HOST="××.××.××.××"
BMC_USER="×××××"
BMC_PASS='×××××'
for c in ipmitool timeout awk; do
command -v "$c" >/dev/null 2>&1 || {
echo "ERROR: 未找到 $c,请在普通 Linux 上安装相关工具" >&2
exit 1
}
done
# 可选 ipmitool 参数,例如某些 BMC 需要:IPMI_EXTRA="-C 17" ./Get_BMC.sh
IPMI_EXTRA_ARGS=()
if [[ -n "${IPMI_EXTRA:-}" ]]; then
read -r -a IPMI_EXTRA_ARGS <<< "$IPMI_EXTRA"
fi
ipmi() {
local -a cmd
# -E 表示从环境变量 IPMI_PASSWORD 读取密码,避免密码出现在命令行参数里
cmd=(timeout 15 ipmitool -I lanplus -H "$BMC_HOST" -U "$BMC_USER" -E)
if ((${#IPMI_EXTRA_ARGS[@]})); then
cmd+=("${IPMI_EXTRA_ARGS[@]}")
fi
cmd+=("$@")
IPMI_PASSWORD="$BMC_PASS" "${cmd[@]}"
}
echo "正在读取 BMC(${BMC_HOST}) 功耗..."
# 方式 1:优先使用 DCMI 标准功耗读数
if dcmi_out="$(ipmi dcmi power reading 2>&1)"; then
power="$(awk -F: '/Instantaneous power reading/ {
v=$2
gsub(/^[[:space:]]+|[[:space:]]+$/, "", v)
print v
exit
}' <<< "$dcmi_out")"
if [[ -n "$power" ]]; then
echo "BMC power: $power"
exit 0
fi
echo "WARN: dcmi 命令成功,但没有解析到 Instantaneous power reading。原始输出:" >&2
echo "$dcmi_out" >&2
else
echo "WARN: dcmi power reading 失败,尝试从 sensor 读取。原始输出:" >&2
echo "$dcmi_out" >&2
fi
完成脚本修改该脚本权限:
bash
chmod 700 Get_BMC.sh
后可直接执行。
2. 每隔10s查看一次服务器其BMC的脚本功率的代码
在上面的代码的基础上,每10s查看服务器BMC脚本:
bash
#!/usr/bin/env bash
# Get_BMC.sh
# 每 10 秒通过 BMC/IPMI 获取一次服务器整机瞬时功耗并打印
#
# 固定目标 BMC IP:××.××.××.××
# 固定用户名:×××××
# 固定密码:×××××
set -euo pipefail
BMC_HOST="××.××.××.××"
BMC_USER="×××××"
BMC_PASS='×××××'
# 采集间隔,默认 10 秒
INTERVAL="${INTERVAL:-10}"
for c in ipmitool timeout awk date sleep; do
command -v "$c" >/dev/null 2>&1 || {
echo "ERROR: 未找到 $c,请在普通 Linux 上安装相关工具" >&2
exit 1
}
done
# 可选 ipmitool 参数,例如:IPMI_EXTRA="-C 17" ./Get_BMC.sh
IPMI_EXTRA_ARGS=()
if [[ -n "${IPMI_EXTRA:-}" ]]; then
read -r -a IPMI_EXTRA_ARGS <<< "$IPMI_EXTRA"
fi
ipmi() {
local -a cmd
# -E 表示从环境变量 IPMI_PASSWORD 读取密码,避免密码出现在命令行参数里
cmd=(timeout 15 ipmitool -I lanplus -H "$BMC_HOST" -U "$BMC_USER" -E)
if ((${#IPMI_EXTRA_ARGS[@]})); then
cmd+=("${IPMI_EXTRA_ARGS[@]}")
fi
cmd+=("$@")
IPMI_PASSWORD="$BMC_PASS" "${cmd[@]}"
}
get_dcmi_power() {
local out power
if ! out="$(ipmi dcmi power reading 2>&1)"; then
return 1
fi
if ! power="$(awk -F: '/Instantaneous power reading/ {
v=$2
gsub(/^[[:space:]]+|[[:space:]]+$/, "", v)
print v
exit
}' <<< "$out")"; then
return 1
fi
if [[ -z "$power" ]]; then
return 1
fi
printf '%s\n' "$power"
}
get_sensor_power() {
local out power
if ! out="$(ipmi sensor 2>&1)"; then
return 1
fi
if ! power="$(awk -F'|' '
BEGIN { best=""; found=0 }
function trim(s) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", s); return s }
function number(s) {
if (match(s, /[0-9]+([.][0-9]+)?/)) return substr(s, RSTART, RLENGTH)
return ""
}
{
name=trim($1); val=trim($2); unit=trim($3)
n=number(val)
low=tolower($0)
u=tolower(unit)
ln=tolower(name)
is_watt=(u ~ /^(w|watt|watts)$/ || low ~ /watt/ || low ~ /[[:space:]]w[[:space:]]/)
if (n != "" && is_watt) {
unit_out=(u ~ /^(w|watt|watts)$/ ? unit : "W")
line=n " " unit_out
# 优先选择整机/总功耗类传感器
if (ln ~ /system|total|power meter|server|pwr consumption|power consumption/) {
print line
found=1
exit
}
if (best == "") best=line
}
}
END { if (!found && best != "") print best }
' <<< "$out")"; then
return 1
fi
if [[ -z "$power" ]]; then
return 1
fi
printf '%s (sensor fallback)\n' "$power"
}
read_power_once() {
local power
if power="$(get_dcmi_power)"; then
printf '%s\n' "$power"
return 0
fi
if power="$(get_sensor_power)"; then
printf '%s\n' "$power"
return 0
fi
return 1
}
trap 'echo; echo "已停止 BMC 功耗采集。"; exit 0' INT TERM
echo "开始每 ${INTERVAL} 秒读取一次 BMC(${BMC_HOST}) 功耗,按 Ctrl+C 停止。"
while true; do
ts="$(date '+%Y-%m-%d %H:%M:%S')"
if power="$(read_power_once)"; then
printf '[%s] BMC(%s) power: %s\n' "$ts" "$BMC_HOST" "$power"
else
printf '[%s] ERROR: 读取 BMC(%s) 功耗失败\n' "$ts" "$BMC_HOST" >&2
fi
sleep "$INTERVAL"
done
EOF
修改该脚本的权限:
bash
chmod 700 Get_BMC.sh
执行该脚本命令。
3.在飞书机器人上面的实时传输服务器BMC功率
在飞书机器人上能够实时传输服务器BMC功率。首先在飞书群内创建一个机器人实时查看BMC功率。
首先在飞书中创建群组,然后在群组中创建自定义机器人:

点击自定义机器人,对于机器人进行命名和描述功能:

点击添加会获得一个之后会有一个webhook地址:https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx
通过shell脚本编写如下代码:
cat > Get_BMC_Feishu.sh <<'EOF'
#!/usr/bin/env bash
# Get_BMC_Feishu.sh
# 每 10 秒读取 BMC 功耗,并输出到飞书群机器人
#
# 固定目标 BMC IP:××.××.××.××
# 固定用户名:×××××
# 固定密码:×××××
set -euo pipefail
BMC_HOST="××.××.××.××"
BMC_USER="×××××"
BMC_PASS='×××××'
# 本地采集间隔,默认 10 秒
INTERVAL="${INTERVAL:-10}"
# 飞书群机器人 Webhook
# 把下面的 xxxxxxxx 替换成你自己的 Webhook 后半段
FEISHU_WEBHOOK="${FEISHU_WEBHOOK:-https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx}"
# 如果飞书机器人开启了"签名校验",把 secret 填到这里
# 如果没开启签名校验,保持为空即可
FEISHU_SECRET="${FEISHU_SECRET:-}"
# 每采集几次发一次飞书
# 1 = 每 10 秒发一条
# 6 = 每 6 条汇总发一次,大约每分钟一次,推荐避免刷屏
FEISHU_SEND_EVERY="${FEISHU_SEND_EVERY:-1}"
# 是否发送启动通知:1 发送,0 不发送
FEISHU_SEND_START_NOTICE="${FEISHU_SEND_START_NOTICE:-1}"
# 是否打印 IPMI 调试输出:1 开启,0 关闭
DEBUG_IPMI="${DEBUG_IPMI:-0}"
for c in ipmitool timeout awk date sleep python3; do
command -v "$c" >/dev/null 2>&1 || {
echo "ERROR: 未找到 $c,请在普通 Linux 上安装相关工具" >&2
exit 1
}
done
if [[ "$FEISHU_WEBHOOK" == *"xxxxxxxx"* ]] || [[ ! "$FEISHU_WEBHOOK" =~ ^https:// ]]; then
echo "ERROR: 请先把 Get_BMC_Feishu.sh 里的 FEISHU_WEBHOOK 改成你的飞书机器人 Webhook 地址。" >&2
exit 1
fi
if ! [[ "$FEISHU_SEND_EVERY" =~ ^[0-9]+$ ]]; then
echo "ERROR: FEISHU_SEND_EVERY 必须是正整数,例如 1 或 6" >&2
exit 1
fi
FEISHU_SEND_EVERY=$((10#$FEISHU_SEND_EVERY))
if (( FEISHU_SEND_EVERY < 1 )); then
echo "ERROR: FEISHU_SEND_EVERY 必须大于 0" >&2
exit 1
fi
if ! [[ "$INTERVAL" =~ ^[0-9]+$ ]]; then
echo "ERROR: INTERVAL 必须是正整数,例如 10" >&2
exit 1
fi
INTERVAL=$((10#$INTERVAL))
if (( INTERVAL < 1 )); then
echo "ERROR: INTERVAL 必须大于 0" >&2
exit 1
fi
# 可选 ipmitool 参数,例如:IPMI_EXTRA="-C 17" ./Get_BMC_Feishu.sh
IPMI_EXTRA_ARGS=()
if [[ -n "${IPMI_EXTRA:-}" ]]; then
read -r -a IPMI_EXTRA_ARGS <<< "$IPMI_EXTRA"
fi
ipmi() {
local -a cmd
# -E 表示从环境变量 IPMI_PASSWORD 读取密码,避免密码出现在命令行参数里
cmd=(timeout 15 ipmitool -I lanplus -H "$BMC_HOST" -U "$BMC_USER" -E)
if ((${#IPMI_EXTRA_ARGS[@]})); then
cmd+=("${IPMI_EXTRA_ARGS[@]}")
fi
cmd+=("$@")
IPMI_PASSWORD="$BMC_PASS" "${cmd[@]}"
}
send_feishu_text() {
local text="$1"
python3 - "$FEISHU_WEBHOOK" "$FEISHU_SECRET" "$text" <<'PY'
import sys
import json
import time
import hmac
import hashlib
import base64
import urllib.request
import urllib.error
webhook, secret, text = sys.argv[1], sys.argv[2], sys.argv[3]
payload = {
"msg_type": "text",
"content": {
"text": text
}
}
# 飞书自定义机器人签名校验
if secret:
timestamp = str(int(time.time()))
string_to_sign = "{}\n{}".format(timestamp, secret)
# 飞书签名算法:
# 以 timestamp + "\n" + secret 作为 HMAC key,对空消息做 HMAC-SHA256,再 Base64
hmac_code = hmac.new(
string_to_sign.encode("utf-8"),
b"",
digestmod=hashlib.sha256
).digest()
sign = base64.b64encode(hmac_code).decode("utf-8")
payload["timestamp"] = timestamp
payload["sign"] = sign
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
req = urllib.request.Request(
webhook,
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=10) as resp:
body = resp.read().decode("utf-8", "replace")
status = resp.getcode()
if status < 200 or status >= 300:
print("Feishu HTTP status:", status, body, file=sys.stderr)
sys.exit(1)
try:
j = json.loads(body)
if not isinstance(j, dict):
j = {}
except Exception:
j = {}
if "StatusCode" in j:
code = j.get("StatusCode")
elif "code" in j:
code = j.get("code")
else:
code = 0
msg = j.get("StatusMessage", j.get("msg", ""))
if code not in (0, "0", None):
print("Feishu API error:", code, msg, body, file=sys.stderr)
sys.exit(1)
except urllib.error.HTTPError as e:
print("Feishu HTTPError:", e.code, e.read().decode("utf-8", "replace"), file=sys.stderr)
sys.exit(1)
except Exception as e:
print("Feishu send failed:", repr(e), file=sys.stderr)
sys.exit(1)
PY
}
get_dcmi_power() {
local out power
if ! out="$(ipmi dcmi power reading 2>&1)"; then
if [[ "$DEBUG_IPMI" == "1" ]]; then
echo "DEBUG: dcmi power reading 失败:$out" >&2
fi
return 1
fi
power="$(awk -F: '/Instantaneous power reading/ {
v=$2
gsub(/^[[:space:]]+|[[:space:]]+$/, "", v)
print v
exit
}' <<< "$out")"
if [[ -z "$power" ]]; then
if [[ "$DEBUG_IPMI" == "1" ]]; then
echo "DEBUG: dcmi 输出未解析到功耗:$out" >&2
fi
return 1
fi
printf '%s\n' "$power"
}
get_sensor_power() {
local out power
if ! out="$(ipmi sensor 2>&1)"; then
if [[ "$DEBUG_IPMI" == "1" ]]; then
echo "DEBUG: ipmitool sensor 失败:$out" >&2
fi
return 1
fi
power="$(awk -F'|' '
BEGIN { best=""; found=0 }
function trim(s) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", s); return s }
function number(s) {
if (match(s, /[0-9]+([.][0-9]+)?/)) return substr(s, RSTART, RLENGTH)
return ""
}
{
name=trim($1); val=trim($2); unit=trim($3)
n=number(val)
low=tolower($0)
u=tolower(unit)
ln=tolower(name)
is_watt=(u ~ /^(w|watt|watts)$/ || low ~ /watt/ || low ~ /[[:space:]]w[[:space:]]/)
if (n != "" && is_watt) {
unit_out = (u ~ /^(w|watt|watts)$/ ? unit : "W")
line=n " " unit_out
# 优先选择整机/总功耗类传感器
if (ln ~ /system|total|power meter|server|pwr consumption|power consumption/) {
print line
found=1
exit
}
if (best == "") best=line
}
}
END { if (!found && best != "") print best }
' <<< "$out")"
if [[ -z "$power" ]]; then
if [[ "$DEBUG_IPMI" == "1" ]]; then
echo "DEBUG: sensor 输出未解析到功耗:$out" >&2
fi
return 1
fi
printf '%s (sensor fallback)\n' "$power"
}
read_power_once() {
local power
if power="$(get_dcmi_power)"; then
printf '%s\n' "$power"
return 0
fi
if power="$(get_sensor_power)"; then
printf '%s\n' "$power"
return 0
fi
return 1
}
declare -a FEISHU_BUFFER=()
flush_feishu_buffer() {
if (( ${#FEISHU_BUFFER[@]} > 0 )); then
local msg
msg="$(printf '%s\n' "${FEISHU_BUFFER[@]}")"
if send_feishu_text "$msg"; then
:
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARN: 发送飞书失败" >&2
fi
FEISHU_BUFFER=()
fi
}
queue_feishu() {
local line="$1"
FEISHU_BUFFER+=("$line")
if (( ${#FEISHU_BUFFER[@]} >= FEISHU_SEND_EVERY )); then
flush_feishu_buffer
fi
}
cleanup() {
flush_feishu_buffer >/dev/null 2>&1 || true
echo
echo "已停止 BMC 功耗采集和飞书上报。"
exit 0
}
trap cleanup INT TERM
if [[ "$FEISHU_SEND_START_NOTICE" == "1" ]]; then
notice="BMC 功耗采集已启动:目标 ${BMC_HOST},本地采集间隔 ${INTERVAL} 秒,飞书每 ${FEISHU_SEND_EVERY} 条采集上报一次。"
if ! send_feishu_text "$notice"; then
echo "WARN: 飞书启动通知发送失败,请检查 Webhook、关键词、IP 白名单或签名 Secret。" >&2
fi
fi
echo "开始每 ${INTERVAL} 秒读取一次 BMC(${BMC_HOST}) 功耗,并输出到飞书,按 Ctrl+C 停止。"
while true; do
cycle_start="$(date +%s)"
ts="$(date '+%Y-%m-%d %H:%M:%S')"
if power="$(read_power_once)"; then
line="[$ts] BMC功耗($BMC_HOST) power: $power"
printf '%s\n' "$line"
else
line="[$ts] ERROR: BMC功耗($BMC_HOST) 读取失败"
printf '%s\n' "$line" >&2
fi
queue_feishu "$line"
cycle_end="$(date +%s)"
elapsed=$((cycle_end - cycle_start))
sleep_for=$((INTERVAL - elapsed))
if (( sleep_for > 0 )); then
sleep "$sleep_for"
fi
done
EOF
然后修改代码权限,即可完成相关操作:
bash
chmod 700 Get_BMC_Feishu.sh
然后开始执行脚本,可以实时查看脚本功率:

4 在飞书机器人上面实现后台运行
想要在后天执行飞书机器人操作,首先先确定脚本位置:
bash
whoami
以及
bash
echo "$HOME"
然后查看脚本权限:
bash
ls -l "$HOME/Get_BMC_Feishu.sh"
查询用户信息:
bash
id 用户
查询完毕之后,写如下所示代码:
bash
sudo tee /etc/systemd/system/bmc-power-feishu.service >/dev/null <<'EOF'
[Unit]
Description=Monitor BMC power and send to Feishu
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=××
WorkingDirectory=/××/××
# 每采集 1 次发一次飞书,约 10 秒一条
# 如果嫌刷屏,可以改成 6,约每分钟汇总发一次
Environment=FEISHU_SEND_EVERY=1
Environment=FEISHU_SEND_START_NOTICE=1
# 如果之前 ipmitool 需要 -C 17,可以取消下面这一行注释
# Environment=IPMI_EXTRA=-C 17
ExecStart=/bin/bash /home/x×/Get_BMC_Feishu.sh
Restart=always
RestartSec=10
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target
EOF
脚本写完之后修改权限:
bash
chmod 700 /home/××/Get_BMC_Feishu.sh
sudo chmod 644 /etc/systemd/system/bmc-power-feishu.service
确认服务文件有没有写错:
bash
grep -nE 'User=|WorkingDirectory=|ExecStart=|Restart=' /etc/systemd/system/bmc-power-feishu.service
然后执行下面这几条指令:
bash
sudo systemctl daemon-reload
sudo systemctl enable bmc-power-feishu.service
sudo systemctl restart bmc-power-feishu.service
sudo systemctl status bmc-power-feishu.service --no-pager
这几条的指令分别是:sudo systemctl daemon-reload,让systemd 重新读一遍磁盘上的 unit 文件,刷新它内存里的配置表;sudo systemctl enable bmc-power-feishu.servic,登记开机自启,本质是在multi-user.target.wants/里建立软链接。sudo systemctl restart bmc-power-feishu.service,按照新配跑起来;sudo systemctl status bmc-power-feishu.service --no-pager验收查看是否成功运行跑起来,正常情况下是active (running)。
现在想要查看最近日志:
bash
sudo journalctl -u bmc-power-feishu.service -n 100 --no-pager
如果想要持续查看日志:
bash
sudo journalctl -u bmc-power-feishu.service -f