ошибка: разрыв канала с скриптом lemonbar

Вопрос или проблема

Я пытаюсь передать скрипт в lemonbar в bspwm.

В моем файле bspwmrc у меня есть:

~/.config/bspwm/lemonbar.sh | lemonbar -p 

lemonbar.sh содержит:

#!/bin/bash

clock() {
    date +%H:%M:%S
}

while true; do
    echo "%{c}$(clock)"
    sleep 1;
done

Все работает как ожидалось, за исключением того, что когда я выхожу из bspwm, я получаю:

line 12: echo: write error: Broken pipe

Очевидно, что ошибка связана с echo на 12 строке, но я не могу понять, в чем именно проблема.

Мне не удалось воспроизвести эту проблему. У меня все работает. Убедитесь, что вы поместили строку со скриптом в конец bspwmrc и добавили в конце &. Вы можете увидеть, как это работает здесь.

У меня есть собственная настройка с lemonbar. Посмотрите в верхнюю часть экрана, время выводится из вашего скрипта, и когда я перезапускаю/выхожу из bspwm, я не получаю никаких ошибок.

Обычно, когда процесс записывает в канал без читателя, этот процесс получает сигнал SIGPIPE и завершает работу. Если процесс настроен игнорировать SIGPIPE, вместо этого он не завершается, но write() возвращает ошибку EPIPE, для которой соответствующее сообщение об ошибке – Broken pipe в английских локалях.

Встроенная команда echo в bash будет сообщать об этой ошибке в этом случае:

$ (trap "" PIPE; bash -c 'sleep 1; echo foo' | true)
bash: line 1: echo: write error: Broken pipe

Таким образом, похоже, что происходит в вашем случае: ваш скрипт был запущен в окружении, где SIGPIPE игнорируется (это не очень мудро в общем).

Изучая исходный код bspwm, это именно то, что он делает. Тот факт, что он не восстанавливает стандартное поведение SIGPIPE при выполнении команд, можно рассматривать как ошибку, о которой вы можете захотеть сообщить.

Чтобы восстановить поведение SIGPIPE на его значение по умолчанию и обойти эту ошибку bspwm, вы можете использовать trap - PIPE, но не в оболочке bash, так как bash учитывает это надоедливое и бессмысленное требование POSIX о том, что сигналы не могут быть восстановлены, если они были проигнорированы при запуске.

Сигналы, которые были проигнорированы при входе в неинтерактивную оболочку, не могут быть перехвачены или сброшены, хотя не требуется сообщать об ошибке, когда вы пытаетесь сделать это. Интерактивная оболочка может сбрасывать или перехватывать сигналы, проигнорированные при входе. Трюки останутся в силе для данной оболочки до тех пор, пока не будут явно изменены другой командой trap.

Таким образом, вам нужно переключиться на другую оболочку, которая не делает этого, например, на zsh:

#! /bin/zsh -
trap - PIPE
while true; do
    print -P '%%{c}%*'
    sleep 1
done

(также используя один из встроенных способов zsh, чтобы вывести текущее время).

Или обработать сбой echo:

#! /bin/bash -

clock() {
  date +%T
}

while true; do
  echo "%{c}$(clock)" 2> /dev/null || exit
  sleep 1
done

Обратите внимание, что если бы вы использовали date +%%{c}%T, так как date не является встроенной командой в bash или zsh, это процесс, выполняющий date, который получил бы сигнал SIGPIPE, когда канал станет сломанным, поэтому вам нужно было бы обработать ошибку в этом случае, независимо от того, игнорируется ли SIGPIPE или нет:

#! /bin/bash -
while true; do
  date +%%{c}%T 2> /dev/null || exit
  sleep 1
done

Если SIGPIPE игнорируется, скрипт завершится, потому что date сообщила о ошибке write() (и завершится нормально с $?, равным примерно 1), а если нет, то потому что date была убита (с $?, равным примерно 141).

Ответ или решение

Ошибка "broken pipe" (разбитая труба) в вашем сценарии связана с тем, что when процесс lemonbar закрывается, ваш скрипт продолжает пытаться выводить данные на "трубку" (pipe), которой больше не существует, и в результате получает сигнал SIGPIPE.

Когда команда в Linux (в данном случае это echo) пытается записать в пайп, который больше не подключён к процессу, который его читает, она получает сигнал SIGPIPE. По умолчанию этот сигнал завершает процесс, но если он был игнорирован, то вместо этого write() возвращает ошибку EPIPE, и сообщение об ошибке "write error: broken pipe" появляется в терминале.

Решение проблемы

  1. Использование trap для обработки SIGPIPE:

    Одна из возможностей — поймать сигнал SIGPIPE и завершить скрипт нормально, если произойдёт ошибка записи. Вы можете использовать конструкцию trap, чтобы обработать эту ситуацию.

    #!/bin/bash
    
    trap 'exit' PIPE
    
    clock() {
       date +%H:%M:%S
    }
    
    while true; do
       echo "%{c}$(clock)"
       sleep 1
    done
  2. Проверка успешности записи:

    Вы можете также проверять успешность команды echo и выходить из скрипта, если запись не удалась.

    #!/bin/bash
    
    clock() {
       date +%H:%M:%S
    }
    
    while true; do
       echo "%{c}$(clock)" || exit
       sleep 1
    done
  3. Использование zsh:

    Поскольку zsh ведёт себя иначе с сигналами, вы можете переписать ваш скрипт на zsh. Это позволит игнорировать SIGPIPE и избежать подобной ошибки, если он возникнет.

    #! /bin/zsh -
    trap - PIPE
    
    while true; do
       print -P "%%{c}$(date +%H:%M:%S)"
       sleep 1
    done

Дополнительные советы

  • Убедитесь, что вызов вашего скрипта в bspwmrc завершён амперсандом (&), чтобы он не блокировал выполнение других команд. Например:

    ~/.config/bspwm/lemonbar.sh | lemonbar -p &
  • Если проблема будет продолжать возникать, рассмотрите возможность обработки ошибок, добавив дополнительный уровень логирования для отслеживания работы скрипта.

Эти подходы помогут вам избежать ошибок, связанных с "broken pipe", и корректно завершать ваш скрипт при выходе из bspwm.

Оцените материал
Добавить комментарий

Капча загружается...