You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Systemd用户服务中Bash Broken Pipe错误排查与修复

问题:systemd用户服务运行Bash脚本时出现Broken Pipe错误

错误现象

终端直接运行脚本正常,但作为systemd用户服务运行时触发Broken Pipe错误,日志如下:

мая 07 10:47:49 nixos systemd[1563]: Starting garmin.service...
мая 07 10:47:51 nixos bash[6951]: grep: ошибка записи: Broken pipe
мая 07 10:47:51 nixos systemd[1563]: garmin.service: Control process exited, code=exited, status=127/n/a
мая 07 10:47:51 nixos systemd[1563]: garmin.service: Failed with result 'exit-code'.
мая 07 10:47:51 nixos systemd[1563]: Failed to start garmin.service.

systemd服务配置

[Unit]
After=run-media-serg-GARMIN.mount
Requires=run-media-serg-GARMIN.mount

[Service]
Environment="LOCALE_ARCHIVE=/nix/store/ckkhm153z75sw6z5nr277kvn2pi4z8jy-glibc-locales-2.35-224/lib/locale/locale-archive"
Environment="PATH=/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/bin:/nix/store/gn1s1s5z19cf0wiir2cd38jckcjc6kn6-findutils-4.9.0/bin:/nix/store/pvb117r7fhwb08717ks21a6y9hlnp63b-gnugrep-3.7/bin:/nix/store/v0hg83sdv4v51c0prmdigry6wdmmpzmp-gnused-4.8/bin:/nix/store/34am2kh69ll6q03731imxf21jdbizda2-systemd-251.15/bin:/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/sbin:/nix/store/gn1s1s5z19cf0wiir2cd38jckcjc6kn6-findutils-4.9.0/sbin:/nix/store/pvb117r7fhwb08717ks21a6y9hlnp63b-gnugrep-3.7/sbin:/nix/store/v0hg83sdv4v51c0prmdigry6wdmmpzmp-gnused-4.8/sbin:/nix/store/34am2kh69ll6q03731imxf21jdbizda2-systemd-251.15/sbin"
Environment="TZDIR=/nix/store/z0kg1c0f8fx6r4rgg5bdy01lb2b9izqg-tzdata-2023a/share/zoneinfo"

ExecStart=/nix/store/kga2r02rmyxl14sg96nxbdhifq3rb8lc-bash-5.1-p16/bin/bash -c /nix/store/jqfh2bnzag50wlghwnw52mq2nzr8bzz2-garmin-backup/bin/garmin-backup
Type=forking

脚本代码

#!/usr/bin/env bash

backupDir="/home/serg/Backup/Garmin Edge 830/"
# 为notify-send导出环境变量
export DISPLAY=:0 
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

on_error() 
{
     _MSG_=$1
     echo "$_MSG_"
     exit 1
}

check_mount()
{
    _MOUNT_=$1
    _MSG_=$2
    
    if [ ! -d "$_MOUNT_" ]; then
        echo "Path to $_MSG_ mount point is not exists"
        exit 1
    fi
}

writeLog() 
{
    _LOG_=$1
    _SAVE_PATH_=$2

    local outputPath="$_SAVE_PATH_/backup_history_${_LOG_}".log

    if [ ! -e "$outputPath" ]; then
        echo "Backup_log: " > "$outputPath"
    fi
    date >> "$outputPath"
}

garmin()
{
    mpointGarmin="$(df -h | grep -i "Garmin" | awk '{print $NF}')"

    check_mount "${mpointGarmin}" "Garmin"
    
    inputDir="${mpointGarmin}/Garmin/"
    copyArray=(
            "Activities" 
            "Locations" 
            "Records" 
            "Settings" 
            "Sports" 
            "Totals" 
            "Weight"
        )
    
    for val in "${copyArray[@]}"
        do
            copyStr+=( --include="$val"/*** )
        done
    
    rsync --dry-run --no-perms --checksum --progress -v -r "${copyStr[@]}" \
         --exclude=* "${inputDir}" "${backupDir}" 
    
    RET_CODE=$?
    
    if test $RET_CODE -eq 0; then
        notify-send "Garmin 830 Backup is Done"
    fi     
}

garmin

已定位问题

问题出在mpointGarmin="$(df -h | grep -i "Garmin" | awk '{print $NF}')"这一行,移除管道子shell、硬编码挂载点后服务可正常运行。疑问:systemd是否不支持带管道的子shell调用?如何修复?


问题原因与修复方案

systemd完全支持管道和子shell,错误的根源有两个:

1. Type=forking配置错误

你的脚本是前台运行的,没有fork后台进程,但Type=forking会让systemd认为主进程退出后服务就已完成,可能提前关闭标准输出/输入流,导致管道中的grep因为输出端被关闭而触发Broken Pipe错误。

修复:将Type=forking改为Type=simple(或直接删除该行,默认就是simple),同时简化ExecStart(脚本已带shebang,无需通过bash -c调用):

ExecStart=/nix/store/jqfh2bnzag50wlghwnw52mq2nzr8bzz2-garmin-backup/bin/garmin-backup

2. df -h解析挂载点的不可靠性

终端环境与systemd服务环境中,df -h的输出可能存在差异(比如locale编码、设备名称格式),导致grep匹配不到内容,后续check_mount检查空路径失败,间接引发管道错误。

修复方案二选一:

  • 方案A(推荐):直接使用固定挂载点
    既然服务已经依赖run-media-serg-GARMIN.mount,挂载点是确定的/run/media/serg/GARMIN,直接硬编码即可:

    mpointGarmin="/run/media/serg/GARMIN"
    

    这是最稳定的方案,避免解析输出的不确定性。

  • 方案B:用findmnt替代df解析
    findmnt是专门用于查询挂载信息的工具,输出格式更稳定,可通过标签或UUID精准获取挂载点:

    # 通过标签获取
    mpointGarmin=$(findmnt -n -o TARGET --label GARMIN)
    # 如果是UUID挂载,替换为:
    # mpointGarmin=$(findmnt -n -o TARGET --uuid <设备UUID>)
    

额外优化

保留脚本中的check_mount函数,即使服务依赖挂载单元,也能二次验证挂载点的存在性,提升鲁棒性。


内容的提问来源于stack exchange,提问作者One

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:12:02