Вопрос или проблема
Я пытаюсь передать скрипт в 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" появляется в терминале.
Решение проблемы
-
Использование
trap
для обработки SIGPIPE:Одна из возможностей — поймать сигнал SIGPIPE и завершить скрипт нормально, если произойдёт ошибка записи. Вы можете использовать конструкцию
trap
, чтобы обработать эту ситуацию.#!/bin/bash trap 'exit' PIPE clock() { date +%H:%M:%S } while true; do echo "%{c}$(clock)" sleep 1 done
-
Проверка успешности записи:
Вы можете также проверять успешность команды
echo
и выходить из скрипта, если запись не удалась.#!/bin/bash clock() { date +%H:%M:%S } while true; do echo "%{c}$(clock)" || exit sleep 1 done
-
Использование
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.