分享喜悦,macbook 免费换了电池和屏幕和键盘 (附刷电池健康脚本)

小豆子 2026-04-21 18:34 1

首先我有 apple care,买了一共四年(第四年快到期了),一共大概花了 3000 块钱不到,21 款的老 m1 pro 了,用了 2 年电池从 100 掉到 83,再用了 2 年 83 掉到 79,真的老吃苦了,我前几天短暂的电池飘红,结果重启了又恢复了气死了,直接开脚本嗯刷掉到 79 了


今天去售后,我和他说除了电池,屏幕上在白色背景下有黑斑(实际上是涂层脱落严重显得有一块很淡的黑斑),本来以为是天才吧不给换的,没想到很好说话,还让我寄修了,电脑继续用


所以现在是换电池,键盘,外壳(下半部分都是统一换的),屏幕,基本上只留大脑,又变成新电脑了 没花一分钱


黑斑如图(无视发绿的部分)


大家要的脚本


前置条件



免责声明:这个完全看运气的,不一定能掉的,我的循环都 1050 了也该跌了


脚本是cc写的,请稍微审阅下,不保证 macos26 是否可用


#!/bin/bash
# ============================================================================
# Battery Cycler Installer — MacBook 电池循环刷坏工具
# ============================================================================
# 用途: 通过自动循环深放 + 满充 + CPU 满载,加速 MacBook 电池健康度下降
# 目的: 在 AC+ 保修期内让 Apple 系统健康度跌破 80%,免费换电池
# for 佬友们
# 不保证最终有效,只保证快速电池重放循环,如有硬件损害请自负责任
# ============================================================================
#
# ⚠️ 使用风险
# - 长期满载 stress-ng 会让机器噪音大、发热
# - 极深放电可能触发 hibernate(机器假死)
# - 对电池是"加速损耗",目的就是把它弄低健康度
# - 效果不保证(苹果黑箱算法疑似长期锁死 80% )
# - 不建议对公司配发的电脑使用
#
# ============================================================================

set -u

# ---------- 前置检查 ----------
echo "╔════════════════════════════════════════╗"
echo "║ Battery Cycler Installer ║"
echo "╚════════════════════════════════════════╝"
echo ""

# Apple Silicon 检查
if [ "$(uname -m)" != "arm64" ]; then
echo "❌ 此工具仅支持 Apple Silicon Mac (M1/M2/M3)"
echo " 当前架构: $(uname -m)"
exit 1
fi

# macOS 版本提醒
OSVER=$(sw_vers -productVersion)
echo "✓ macOS $OSVER"
echo "✓ 芯片: $(sysctl -n machdep.cpu.brand_string)"

# 自动算 stress 核数(总核数 - 1,留一个给系统)
TOTAL_CORES=$(sysctl -n hw.ncpu)
STRESS_CORES=$((TOTAL_CORES - 1))
echo "✓ CPU 核数: $TOTAL_CORES (将使用 $STRESS_CORES 核跑 stress-ng)"
echo ""

# ---------- 依赖检查 ----------
echo "▶ 检查依赖..."
MISSING=()

if ! command -v brew >/dev/null 2>&1; then
echo "❌ 没装 Homebrew,先装:https://brew.sh"
exit 1
fi

if ! command -v battery >/dev/null 2>&1; then
MISSING+=("battery")
fi

if ! command -v stress-ng >/dev/null 2>&1; then
MISSING+=("stress-ng")
fi

if [ ${#MISSING[@]} -gt 0 ]; then
echo "⚠️ 缺少依赖: ${MISSING[*]}"
echo ""
read -p "自动用 brew 装吗?(y/n): " ans
if [ "$ans" = "y" ]; then
for dep in "${MISSING[@]}"; do
if [ "$dep" = "battery" ]; then
brew install --cask battery
else
brew install "$dep"
fi
done
else
echo "❌ 请手动装完依赖再运行本脚本"
exit 1
fi
fi
echo "✓ 依赖齐全"
echo ""

# ---------- battery CLI 授权检查 ----------
echo "▶ 检查 battery CLI 授权..."
if ! battery status >/dev/null 2>&1; then
echo "⚠️ battery CLI 尚未授权"
echo ""
echo " 请执行以下两步:"
echo " 1. 打开 /Applications/battery.app 一次,按提示输密码装 helper"
echo " 2. 在 battery app Preferences 里勾选 '✅ Allow force-discharging'"
echo ""
read -p "做完后按 Enter 继续..."
fi

if ! battery status >/dev/null 2>&1; then
echo "❌ battery CLI 仍不可用,无法继续"
exit 1
fi
echo "✓ battery CLI OK"
echo ""

# ---------- 写脚本 ----------
USER_BIN="$HOME/bin"
mkdir -p "$USER_BIN"
SCRIPT="$USER_BIN/battery-cycler.sh"
STOP_SCRIPT="$USER_BIN/battery-cycler-stop.sh"
BSTAT="$USER_BIN/bstat"
BPWR="$USER_BIN/bpwr"
PLIST_LABEL="com.$(whoami).battery-cycler"
PLIST="$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist"

echo "▶ 写入脚本到 $USER_BIN/"

cat > "$SCRIPT" <<EOF
#!/bin/bash
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
set -u

LOW=1
HIGH=100
STRESS_CPU="$STRESS_CORES"
LOG="\$HOME/battery-cycler.log"
STRESS_LOG="\$HOME/battery-cycler.stress.log"
STRESS_PATTERN="stress-ng --cpu \${STRESS_CPU}"

log() { echo "[\$(date '+%F %T')] \$*" >> "\$LOG"; }
pct() { pmset -g batt | grep -Eo '[0-9]+%' | head -1 | tr -d '%'; }

log_health() {
local apple_pct cycles nominal raw design gauge_pct raw_pct
apple_pct=\$(system_profiler SPPowerDataType 2>/dev/null | awk '/Maximum Capacity/{gsub("%","",\$3); print \$3; exit}')
cycles=\$(system_profiler SPPowerDataType 2>/dev/null | awk '/Cycle Count/{print \$3; exit}')
nominal=\$(ioreg -rc AppleSmartBattery 2>/dev/null | awk '/^ +"NominalChargeCapacity" = [0-9]+\$/{print \$NF; exit}')
raw=\$(ioreg -rc AppleSmartBattery 2>/dev/null | awk '/^ +"AppleRawMaxCapacity" = [0-9]+\$/{print \$NF; exit}')
design=\$(ioreg -rc AppleSmartBattery 2>/dev/null | awk '/^ +"DesignCapacity" = [0-9]+\$/{print \$NF; exit}')
gauge_pct=\$(awk -v n="\$nominal" -v d="\$design" 'BEGIN{printf "%.2f", n/d*100}')
raw_pct=\$(awk -v r="\$raw" -v d="\$design" 'BEGIN{printf "%.2f", r/d*100}')
log "HEALTH | Apple: \${apple_pct}% | Raw: \${raw_pct}% (\${raw} mAh) | Gauge: \${gauge_pct}% (\${nominal}/\${design} mAh) | Cycles: \${cycles}"
}

start_stress() {
if ! pgrep -f "\$STRESS_PATTERN" >/dev/null; then
stress-ng --cpu "\$STRESS_CPU" --timeout 0 --temp-path /tmp >> "\$STRESS_LOG" 2>&1 &
log "stress-ng 启动 PID=\$!"
fi
}

stop_stress() {
if pgrep -f "\$STRESS_PATTERN" >/dev/null; then
pkill -f "\$STRESS_PATTERN" 2>/dev/null
log "stress-ng 已停止"
fi
}

cleanup() {
log "退出:恢复充电,杀 stress-ng"
battery charging on >/dev/null 2>&1
battery adapter on >/dev/null 2>&1
pkill -f "\$STRESS_PATTERN" 2>/dev/null
exit 0
}
trap cleanup INT TERM EXIT

cd "\$HOME"
log "========== 启动 =========="

n=0
while true; do
n=\$((n+1))
log "=== 第 \$n 轮 ==="
log_health

log "→ 放电阶段(adapter off + stress-ng 满载 \${STRESS_CPU} 核,目标 ≤\${LOW}%)"
battery adapter off >/dev/null 2>&1
start_stress
stress_stopped_emergency=0
while true; do
p=\$(pct)
[ -z "\$p" ] && { sleep 5; continue; }

if [ "\$p" -le 2 ] && [ "\$stress_stopped_emergency" -eq 0 ]; then
log "⚠️ 电量 ≤2%,紧急停 stress-ng 降负载"
stop_stress
stress_stopped_emergency=1
fi

[ "\$stress_stopped_emergency" -eq 0 ] && start_stress

log "当前 \${p}% [放电]"
[ "\$p" -le "\$LOW" ] && break

if [ "\$p" -le 3 ]; then sleep 1
elif [ "\$p" -le 8 ]; then sleep 10
elif [ "\$p" -le 15 ]; then sleep 20
else sleep 40
fi
done

log "→ 充电阶段(暂停 stress-ng,目标 ≥\${HIGH}%)"
stop_stress
battery adapter on >/dev/null 2>&1
battery charging on >/dev/null 2>&1
while true; do
p=\$(pct)
[ -z "\$p" ] && { sleep 30; continue; }
log "当前 \${p}% [充电]"
[ "\$p" -ge "\$HIGH" ] && break
sleep 120
done

log "→ 第 \$n 轮完成"
log_health
done
EOF
chmod +x "$SCRIPT"
echo " ✓ $SCRIPT"

# 停止脚本
cat > "$STOP_SCRIPT" <<EOF
#!/bin/bash
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
PLIST="\$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist"

echo "→ 停止 LaunchAgent..."
launchctl unload "\$PLIST" 2>/dev/null && echo " ✓ unloaded" || echo " (未加载)"

echo "→ 杀 stress-ng..."
pkill -f 'stress-ng --cpu' 2>/dev/null && echo " ✓ killed" || echo " (未运行)"

echo "→ 杀 cycler 脚本..."
pkill -f battery-cycler.sh 2>/dev/null && echo " ✓ killed" || echo " (未运行)"

echo "→ 恢复充电..."
battery adapter on >/dev/null 2>&1 && echo " ✓ adapter on"
battery charging on >/dev/null 2>&1 && echo " ✓ charging on"

if [ "\${1:-}" = "--purge" ]; then
echo "→ 彻底清理..."
rm -f "\$PLIST" \$HOME/bin/battery-cycler.sh \$HOME/bin/battery-cycler-stop.sh
rm -f \$HOME/battery-cycler.log \$HOME/battery-cycler.{stress,out,err}.log
echo " ✓ 全部删除"
fi
echo ""
echo "✓ 完成"
EOF
chmod +x "$STOP_SCRIPT"
echo " ✓ $STOP_SCRIPT"

# bstat 工具
cat > "$BSTAT" <<'EOF'
#!/bin/bash
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
echo "╔══════════════════════════════╗"
echo "║ 电池状态看板 ║"
echo "╚══════════════════════════════╝"
pmset -g batt | tail -1
echo ""
system_profiler SPPowerDataType | grep -E "State of Charge|Cycle Count|Condition|Maximum Capacity" | head -8
echo ""
VOLT=$(ioreg -rc AppleSmartBattery | awk '/^ +"Voltage" = /{print $NF; exit}')
AMP=$(( $(ioreg -rc AppleSmartBattery | awk '/^ +"InstantAmperage" = /{print $NF; exit}') ))
TEMP=$(ioreg -rc AppleSmartBattery | awk '/^ +"Temperature" = /{print $NF; exit}')
NOMINAL=$(ioreg -rc AppleSmartBattery | awk '/^ +"NominalChargeCapacity" = [0-9]+$/{print $NF; exit}')
RAW=$(ioreg -rc AppleSmartBattery | awk '/^ +"AppleRawMaxCapacity" = [0-9]+$/{print $NF; exit}')
DESIGN=$(ioreg -rc AppleSmartBattery | awk '/^ +"DesignCapacity" = [0-9]+$/{print $NF; exit}')
CYCLE=$(ioreg -rc AppleSmartBattery | awk '/^ +"CycleCount" = [0-9]+$/{print $NF; exit}')
PHYS=$(awk -v n="$NOMINAL" -v d="$DESIGN" 'BEGIN{printf "%.2f", n/d*100}')
RAW_PCT=$(awk -v r="$RAW" -v d="$DESIGN" 'BEGIN{printf "%.2f", r/d*100}')
TEMP_C=$(awk -v t="$TEMP" 'BEGIN{printf "%.1f", t/100}')
printf " 电压: %.3f V\n" "$(awk -v v="$VOLT" 'BEGIN{printf "%.3f", v/1000}')"
printf " 电流: %d mA\n" "$AMP"
printf " 温度: %s°C\n" "$TEMP_C"
printf " gauge 估算: %s mAh (%s%%)\n" "$NOMINAL" "$PHYS"
printf " 苹果 Raw: %s mAh (%s%%) ⭐\n" "$RAW" "$RAW_PCT"
printf " 设计容量: %s mAh\n" "$DESIGN"
printf " 循环次数: %s\n" "$CYCLE"
EOF
chmod +x "$BSTAT"
echo " ✓ $BSTAT"

# bpwr 工具
cat > "$BPWR" <<'EOF'
#!/bin/bash
AMP_RAW=$(ioreg -rc AppleSmartBattery | awk '/^ +"InstantAmperage" = /{print $NF; exit}')
VOLT=$(ioreg -rc AppleSmartBattery | awk '/^ +"Voltage" = /{print $NF; exit}')
AMP=$((AMP_RAW))
awk -v amp="$AMP" -v volt="$VOLT" 'BEGIN{
p = amp * volt / 1000000
s = (p < 0) ? "充电" : (p > 0 ? "放电" : "稳态")
if (p < 0) p = -p
printf "%s | %d mA | %d mV | %.2f W\n", s, amp, volt, p
}'
EOF
chmod +x "$BPWR"
echo " ✓ $BPWR"

# ---------- 写 plist ----------
echo ""
echo "▶ 写入 LaunchAgent: $PLIST"
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${PLIST_LABEL}</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/caffeinate</string>
<string>-ims</string>
<string>${SCRIPT}</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>30</integer>
<key>StandardOutPath</key>
<string>${HOME}/battery-cycler.out.log</string>
<key>StandardErrorPath</key>
<string>${HOME}/battery-cycler.err.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
</dict>
</plist>
EOF
echo " ✓ plist 已写入"

# ---------- PATH 检查 ----------
echo ""
if ! echo "$PATH" | grep -q "$HOME/bin"; then
echo "▶ ~/bin 不在 PATH,追加到 ~/.zshrc"
echo '' >> ~/.zshrc
echo '# Battery Cycler 工具' >> ~/.zshrc
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.zshrc
echo " ✓ 已追加,重开终端或跑 source ~/.zshrc 生效"
else
echo "✓ ~/bin 已在 PATH"
fi

# ---------- 完成提示 ----------
echo ""
echo "╔════════════════════════════════════════╗"
echo "║ ✓ 安装完成 ║"
echo "╚════════════════════════════════════════╝"
echo ""
echo "【启动循环】(立即开始刷电池,机器会一直满载跑)"
echo " launchctl load $PLIST"
echo ""
echo "【停止】"
echo " battery-cycler-stop.sh # 暂停"
echo " battery-cycler-stop.sh --purge # 彻底卸载(删脚本+日志)"
echo ""
echo "【查看状态】"
echo " bstat # 电池状态看板"
echo " bpwr # 实时功率"
echo " tail -f ~/battery-cycler.log # 实时日志"
echo " grep HEALTH ~/battery-cycler.log # 所有 HEALTH 记录"
echo ""
echo " - 一旦跳了或者 Condition 变成 Service Recommended → 立刻去天才吧换电池"
echo ""
echo "⚠️ 警告:"
echo " 1. 机器会持续满载 + 风扇狂转(stress-ng ${STRESS_CORES} 核)"
echo " 2. 电池真的会被伤坏(这是目的)"
echo " 3. 循环深度到 1%,有可能触发空电假死,得按电源键启动"
echo " 4. 不建议在公司机 / 家人共用机跑"
echo " 5. 效果不保证 —— 苹果黑箱健康度算法有可能短期刷不动"
echo ""
echo "祝电池早日跌下80%! 🔋💥"


我只在 macos 15 上测试过,macos26 不一定可用


有疑问请询问 ai,也是 ai 写的,也可以让 ai 来审查


拿到文件后在他自己的 Mac 上执行:


bash ~/Downloads/battery-cycler-installer.sh (改成实际路径)


可能需要 chmod +x 授权下


安装完成后朋友要主动跑一条命令才开始:


launchctl load ~/Library/LaunchAgents/com.$USER.battery-cycler.plist

最新回复 (19)
  • shizhongjian 04-21 18:35
    1楼

    什么意思哦,免费的吗为什么给你免费换呀,分享来看看

  • 小豆子 楼主 04-21 18:36
    2楼

    忘记说了我有 apple care,当然如果没过保应该也是可以免费换新的

  • zikhir 04-21 18:37
    3楼

    刚想问你这apple care还在保吗,你这是买了多久的apple care?当时花了多少

  • 小豆子 楼主 04-21 18:38
    4楼

    4 年,第1-3年 2000 多一点,第四年订阅是 900

  • cherry_blueberry 04-21 18:40
    5楼

    脚本分享下唄,我的电池一直处于80%都快一年啦还没掉。ac今年6月份到期。实在不行按年续ac

  • lafish 04-21 18:41
    6楼

    同款,上个月刚续费ac,849,电池还坚挺80+。如果我一直续费,活到产品配件停产,是不是就能给我换一个全新的了 ^-^

  • 小豆子 楼主 04-21 18:41
    7楼

    我找找,ai 写的,你也可以让 ai 给你写,续费无所谓的,可以按天退的

  • 小豆子 楼主 04-21 18:43
    8楼

    那倒是没必要,一直续费估计还得续个四年,又要 三四千 块钱了,那为什么不买个新的,你还电池基本上就能换下半部分全套了

  • 小豆子 楼主 04-21 18:47
    9楼

    前置条件



    免责声明:这个完全看运气的,不一定能掉的,我的循环都 1050 了也该跌了


    脚本是cc写的,请稍微审阅下,不保证 macos26 是否可用


    #!/bin/bash
    # ============================================================================
    # Battery Cycler Installer — MacBook 电池循环刷坏工具
    # ============================================================================
    # 用途: 通过自动循环深放 + 满充 + CPU 满载,加速 MacBook 电池健康度下降
    # 目的: 在 AC+ 保修期内让 Apple 系统健康度跌破 80%,免费换电池
    # for 佬友们
    # 不保证最终有效,只保证快速电池重放循环,如有硬件损害请自负责任
    # ============================================================================
    #
    # ⚠️ 使用风险
    # - 长期满载 stress-ng 会让机器噪音大、发热
    # - 极深放电可能触发 hibernate(机器假死)
    # - 对电池是"加速损耗",目的就是把它弄低健康度
    # - 效果不保证(苹果黑箱算法疑似长期锁死 80% )
    # - 不建议对公司配发的电脑使用
    #
    # ============================================================================

    set -u

    # ---------- 前置检查 ----------
    echo "╔════════════════════════════════════════╗"
    echo "║ Battery Cycler Installer ║"
    echo "╚════════════════════════════════════════╝"
    echo ""

    # Apple Silicon 检查
    if [ "$(uname -m)" != "arm64" ]; then
    echo "❌ 此工具仅支持 Apple Silicon Mac (M1/M2/M3)"
    echo " 当前架构: $(uname -m)"
    exit 1
    fi

    # macOS 版本提醒
    OSVER=$(sw_vers -productVersion)
    echo "✓ macOS $OSVER"
    echo "✓ 芯片: $(sysctl -n machdep.cpu.brand_string)"

    # 自动算 stress 核数(总核数 - 1,留一个给系统)
    TOTAL_CORES=$(sysctl -n hw.ncpu)
    STRESS_CORES=$((TOTAL_CORES - 1))
    echo "✓ CPU 核数: $TOTAL_CORES (将使用 $STRESS_CORES 核跑 stress-ng)"
    echo ""

    # ---------- 依赖检查 ----------
    echo "▶ 检查依赖..."
    MISSING=()

    if ! command -v brew >/dev/null 2>&1; then
    echo "❌ 没装 Homebrew,先装:https://brew.sh"
    exit 1
    fi

    if ! command -v battery >/dev/null 2>&1; then
    MISSING+=("battery")
    fi

    if ! command -v stress-ng >/dev/null 2>&1; then
    MISSING+=("stress-ng")
    fi

    if [ ${#MISSING[@]} -gt 0 ]; then
    echo "⚠️ 缺少依赖: ${MISSING[*]}"
    echo ""
    read -p "自动用 brew 装吗?(y/n): " ans
    if [ "$ans" = "y" ]; then
    for dep in "${MISSING[@]}"; do
    if [ "$dep" = "battery" ]; then
    brew install --cask battery
    else
    brew install "$dep"
    fi
    done
    else
    echo "❌ 请手动装完依赖再运行本脚本"
    exit 1
    fi
    fi
    echo "✓ 依赖齐全"
    echo ""

    # ---------- battery CLI 授权检查 ----------
    echo "▶ 检查 battery CLI 授权..."
    if ! battery status >/dev/null 2>&1; then
    echo "⚠️ battery CLI 尚未授权"
    echo ""
    echo " 请执行以下两步:"
    echo " 1. 打开 /Applications/battery.app 一次,按提示输密码装 helper"
    echo " 2. 在 battery app Preferences 里勾选 '✅ Allow force-discharging'"
    echo ""
    read -p "做完后按 Enter 继续..."
    fi

    if ! battery status >/dev/null 2>&1; then
    echo "❌ battery CLI 仍不可用,无法继续"
    exit 1
    fi
    echo "✓ battery CLI OK"
    echo ""

    # ---------- 写脚本 ----------
    USER_BIN="$HOME/bin"
    mkdir -p "$USER_BIN"
    SCRIPT="$USER_BIN/battery-cycler.sh"
    STOP_SCRIPT="$USER_BIN/battery-cycler-stop.sh"
    BSTAT="$USER_BIN/bstat"
    BPWR="$USER_BIN/bpwr"
    PLIST_LABEL="com.$(whoami).battery-cycler"
    PLIST="$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist"

    echo "▶ 写入脚本到 $USER_BIN/"

    cat > "$SCRIPT" <<EOF
    #!/bin/bash
    export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    set -u

    LOW=1
    HIGH=100
    STRESS_CPU="$STRESS_CORES"
    LOG="\$HOME/battery-cycler.log"
    STRESS_LOG="\$HOME/battery-cycler.stress.log"
    STRESS_PATTERN="stress-ng --cpu \${STRESS_CPU}"

    log() { echo "[\$(date '+%F %T')] \$*" >> "\$LOG"; }
    pct() { pmset -g batt | grep -Eo '[0-9]+%' | head -1 | tr -d '%'; }

    log_health() {
    local apple_pct cycles nominal raw design gauge_pct raw_pct
    apple_pct=\$(system_profiler SPPowerDataType 2>/dev/null | awk '/Maximum Capacity/{gsub("%","",\$3); print \$3; exit}')
    cycles=\$(system_profiler SPPowerDataType 2>/dev/null | awk '/Cycle Count/{print \$3; exit}')
    nominal=\$(ioreg -rc AppleSmartBattery 2>/dev/null | awk '/^ +"NominalChargeCapacity" = [0-9]+\$/{print \$NF; exit}')
    raw=\$(ioreg -rc AppleSmartBattery 2>/dev/null | awk '/^ +"AppleRawMaxCapacity" = [0-9]+\$/{print \$NF; exit}')
    design=\$(ioreg -rc AppleSmartBattery 2>/dev/null | awk '/^ +"DesignCapacity" = [0-9]+\$/{print \$NF; exit}')
    gauge_pct=\$(awk -v n="\$nominal" -v d="\$design" 'BEGIN{printf "%.2f", n/d*100}')
    raw_pct=\$(awk -v r="\$raw" -v d="\$design" 'BEGIN{printf "%.2f", r/d*100}')
    log "HEALTH | Apple: \${apple_pct}% | Raw: \${raw_pct}% (\${raw} mAh) | Gauge: \${gauge_pct}% (\${nominal}/\${design} mAh) | Cycles: \${cycles}"
    }

    start_stress() {
    if ! pgrep -f "\$STRESS_PATTERN" >/dev/null; then
    stress-ng --cpu "\$STRESS_CPU" --timeout 0 --temp-path /tmp >> "\$STRESS_LOG" 2>&1 &
    log "stress-ng 启动 PID=\$!"
    fi
    }

    stop_stress() {
    if pgrep -f "\$STRESS_PATTERN" >/dev/null; then
    pkill -f "\$STRESS_PATTERN" 2>/dev/null
    log "stress-ng 已停止"
    fi
    }

    cleanup() {
    log "退出:恢复充电,杀 stress-ng"
    battery charging on >/dev/null 2>&1
    battery adapter on >/dev/null 2>&1
    pkill -f "\$STRESS_PATTERN" 2>/dev/null
    exit 0
    }
    trap cleanup INT TERM EXIT

    cd "\$HOME"
    log "========== 启动 =========="

    n=0
    while true; do
    n=\$((n+1))
    log "=== 第 \$n 轮 ==="
    log_health

    log "→ 放电阶段(adapter off + stress-ng 满载 \${STRESS_CPU} 核,目标 ≤\${LOW}%)"
    battery adapter off >/dev/null 2>&1
    start_stress
    stress_stopped_emergency=0
    while true; do
    p=\$(pct)
    [ -z "\$p" ] && { sleep 5; continue; }

    if [ "\$p" -le 2 ] && [ "\$stress_stopped_emergency" -eq 0 ]; then
    log "⚠️ 电量 ≤2%,紧急停 stress-ng 降负载"
    stop_stress
    stress_stopped_emergency=1
    fi

    [ "\$stress_stopped_emergency" -eq 0 ] && start_stress

    log "当前 \${p}% [放电]"
    [ "\$p" -le "\$LOW" ] && break

    if [ "\$p" -le 3 ]; then sleep 1
    elif [ "\$p" -le 8 ]; then sleep 10
    elif [ "\$p" -le 15 ]; then sleep 20
    else sleep 40
    fi
    done

    log "→ 充电阶段(暂停 stress-ng,目标 ≥\${HIGH}%)"
    stop_stress
    battery adapter on >/dev/null 2>&1
    battery charging on >/dev/null 2>&1
    while true; do
    p=\$(pct)
    [ -z "\$p" ] && { sleep 30; continue; }
    log "当前 \${p}% [充电]"
    [ "\$p" -ge "\$HIGH" ] && break
    sleep 120
    done

    log "→ 第 \$n 轮完成"
    log_health
    done
    EOF
    chmod +x "$SCRIPT"
    echo " ✓ $SCRIPT"

    # 停止脚本
    cat > "$STOP_SCRIPT" <<EOF
    #!/bin/bash
    export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    PLIST="\$HOME/Library/LaunchAgents/${PLIST_LABEL}.plist"

    echo "→ 停止 LaunchAgent..."
    launchctl unload "\$PLIST" 2>/dev/null && echo " ✓ unloaded" || echo " (未加载)"

    echo "→ 杀 stress-ng..."
    pkill -f 'stress-ng --cpu' 2>/dev/null && echo " ✓ killed" || echo " (未运行)"

    echo "→ 杀 cycler 脚本..."
    pkill -f battery-cycler.sh 2>/dev/null && echo " ✓ killed" || echo " (未运行)"

    echo "→ 恢复充电..."
    battery adapter on >/dev/null 2>&1 && echo " ✓ adapter on"
    battery charging on >/dev/null 2>&1 && echo " ✓ charging on"

    if [ "\${1:-}" = "--purge" ]; then
    echo "→ 彻底清理..."
    rm -f "\$PLIST" \$HOME/bin/battery-cycler.sh \$HOME/bin/battery-cycler-stop.sh
    rm -f \$HOME/battery-cycler.log \$HOME/battery-cycler.{stress,out,err}.log
    echo " ✓ 全部删除"
    fi
    echo ""
    echo "✓ 完成"
    EOF
    chmod +x "$STOP_SCRIPT"
    echo " ✓ $STOP_SCRIPT"

    # bstat 工具
    cat > "$BSTAT" <<'EOF'
    #!/bin/bash
    export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    echo "╔══════════════════════════════╗"
    echo "║ 电池状态看板 ║"
    echo "╚══════════════════════════════╝"
    pmset -g batt | tail -1
    echo ""
    system_profiler SPPowerDataType | grep -E "State of Charge|Cycle Count|Condition|Maximum Capacity" | head -8
    echo ""
    VOLT=$(ioreg -rc AppleSmartBattery | awk '/^ +"Voltage" = /{print $NF; exit}')
    AMP=$(( $(ioreg -rc AppleSmartBattery | awk '/^ +"InstantAmperage" = /{print $NF; exit}') ))
    TEMP=$(ioreg -rc AppleSmartBattery | awk '/^ +"Temperature" = /{print $NF; exit}')
    NOMINAL=$(ioreg -rc AppleSmartBattery | awk '/^ +"NominalChargeCapacity" = [0-9]+$/{print $NF; exit}')
    RAW=$(ioreg -rc AppleSmartBattery | awk '/^ +"AppleRawMaxCapacity" = [0-9]+$/{print $NF; exit}')
    DESIGN=$(ioreg -rc AppleSmartBattery | awk '/^ +"DesignCapacity" = [0-9]+$/{print $NF; exit}')
    CYCLE=$(ioreg -rc AppleSmartBattery | awk '/^ +"CycleCount" = [0-9]+$/{print $NF; exit}')
    PHYS=$(awk -v n="$NOMINAL" -v d="$DESIGN" 'BEGIN{printf "%.2f", n/d*100}')
    RAW_PCT=$(awk -v r="$RAW" -v d="$DESIGN" 'BEGIN{printf "%.2f", r/d*100}')
    TEMP_C=$(awk -v t="$TEMP" 'BEGIN{printf "%.1f", t/100}')
    printf " 电压: %.3f V\n" "$(awk -v v="$VOLT" 'BEGIN{printf "%.3f", v/1000}')"
    printf " 电流: %d mA\n" "$AMP"
    printf " 温度: %s°C\n" "$TEMP_C"
    printf " gauge 估算: %s mAh (%s%%)\n" "$NOMINAL" "$PHYS"
    printf " 苹果 Raw: %s mAh (%s%%) ⭐\n" "$RAW" "$RAW_PCT"
    printf " 设计容量: %s mAh\n" "$DESIGN"
    printf " 循环次数: %s\n" "$CYCLE"
    EOF
    chmod +x "$BSTAT"
    echo " ✓ $BSTAT"

    # bpwr 工具
    cat > "$BPWR" <<'EOF'
    #!/bin/bash
    AMP_RAW=$(ioreg -rc AppleSmartBattery | awk '/^ +"InstantAmperage" = /{print $NF; exit}')
    VOLT=$(ioreg -rc AppleSmartBattery | awk '/^ +"Voltage" = /{print $NF; exit}')
    AMP=$((AMP_RAW))
    awk -v amp="$AMP" -v volt="$VOLT" 'BEGIN{
    p = amp * volt / 1000000
    s = (p < 0) ? "充电" : (p > 0 ? "放电" : "稳态")
    if (p < 0) p = -p
    printf "%s | %d mA | %d mV | %.2f W\n", s, amp, volt, p
    }'
    EOF
    chmod +x "$BPWR"
    echo " ✓ $BPWR"

    # ---------- 写 plist ----------
    echo ""
    echo "▶ 写入 LaunchAgent: $PLIST"
    cat > "$PLIST" <<EOF
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
    <key>Label</key>
    <string>${PLIST_LABEL}</string>
    <key>ProgramArguments</key>
    <array>
    <string>/usr/bin/caffeinate</string>
    <string>-ims</string>
    <string>${SCRIPT}</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>ThrottleInterval</key>
    <integer>30</integer>
    <key>StandardOutPath</key>
    <string>${HOME}/battery-cycler.out.log</string>
    <key>StandardErrorPath</key>
    <string>${HOME}/battery-cycler.err.log</string>
    <key>EnvironmentVariables</key>
    <dict>
    <key>PATH</key>
    <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
    </dict>
    </dict>
    </plist>
    EOF
    echo " ✓ plist 已写入"

    # ---------- PATH 检查 ----------
    echo ""
    if ! echo "$PATH" | grep -q "$HOME/bin"; then
    echo "▶ ~/bin 不在 PATH,追加到 ~/.zshrc"
    echo '' >> ~/.zshrc
    echo '# Battery Cycler 工具' >> ~/.zshrc
    echo 'export PATH="$HOME/bin:$PATH"' >> ~/.zshrc
    echo " ✓ 已追加,重开终端或跑 source ~/.zshrc 生效"
    else
    echo "✓ ~/bin 已在 PATH"
    fi

    # ---------- 完成提示 ----------
    echo ""
    echo "╔════════════════════════════════════════╗"
    echo "║ ✓ 安装完成 ║"
    echo "╚════════════════════════════════════════╝"
    echo ""
    echo "【启动循环】(立即开始刷电池,机器会一直满载跑)"
    echo " launchctl load $PLIST"
    echo ""
    echo "【停止】"
    echo " battery-cycler-stop.sh # 暂停"
    echo " battery-cycler-stop.sh --purge # 彻底卸载(删脚本+日志)"
    echo ""
    echo "【查看状态】"
    echo " bstat # 电池状态看板"
    echo " bpwr # 实时功率"
    echo " tail -f ~/battery-cycler.log # 实时日志"
    echo " grep HEALTH ~/battery-cycler.log # 所有 HEALTH 记录"
    echo ""
    echo " - 一旦跳了或者 Condition 变成 Service Recommended → 立刻去天才吧换电池"
    echo ""
    echo "⚠️ 警告:"
    echo " 1. 机器会持续满载 + 风扇狂转(stress-ng ${STRESS_CORES} 核)"
    echo " 2. 电池真的会被伤坏(这是目的)"
    echo " 3. 循环深度到 1%,有可能触发空电假死,得按电源键启动"
    echo " 4. 不建议在公司机 / 家人共用机跑"
    echo " 5. 效果不保证 —— 苹果黑箱健康度算法有可能短期刷不动"
    echo ""
    echo "祝电池早日跌下80%! 🔋💥"


    我只在 macos 15 上测试过,macos26 不一定可用


    有疑问请询问 ai,也是 ai 写的,也可以让 ai 来审查


    拿到文件后在他自己的 Mac 上执行:


    bash ~/Downloads/battery-cycler-installer.sh (改成实际路径)


    可能需要 chmod +x 授权下


    安装完成后朋友要主动跑一条命令才开始:


    launchctl load ~/Library/LaunchAgents/com.$USER.battery-cycler.plist

  • 霜冻舞者 04-21 18:58
    10楼

    等脚本了,没想到还有这么厉害的脚本,正好今年低也要到期了。

  • 小豆子 楼主 04-21 19:03
    11楼

    发了啊,看上面,只不过这个玩意不保证能降低的,本质是用 1 个半小时放电,再用一个半小时充电,过充过放,开机自启,自动循环

  • Edward 04-21 19:06
    12楼

    我跟佬差不多,不过ac应该过期了,我一直都是用那个AIDENT的软件,充电到80%就用充电供电,不用电池供电,现在依旧坚挺着

  • 小豆子 楼主 04-21 19:08
    13楼

    macos26.4 有系统级的电池管理了

  • 霜冻舞者 04-21 19:11
    14楼

    已加入书签等时间到了就用它试试,手工费收了多少咯?

  • Edward 04-21 19:12
    15楼

    没升级到最新系统,我是非必要不升级,不然bug确实让人难受

  • 小豆子 楼主 04-21 19:20
    16楼

    全部 0 元,这个属于是他们质量问题啊(或者说属于质保范畴),不是我人为的

  • 小豆子 楼主 04-21 19:21
    17楼

    换屏幕他就让我升级了,不然数据写不进去,难绷

  • Edward 04-21 19:23
    18楼

    这样的么,涨知识了,看看还能再战多久,M5出来的时候有点心动想换的,哈哈

  • 小豆子 楼主 04-21 19:27
    20楼

    编辑到主楼了,麻烦你看下(总感觉自己发了好多次了)

* 帖子来源Linux.do
返回