#linux #shell #awk #io #pipe
#linux #оболочка #awk #io #канал
Вопрос:
Моя система — Arch Linux, а мой оконный менеджер — DWM. Я использую dash в качестве интерпретатора оболочки.
Я написал этот сценарий оболочки расширения для своего таймера.
xev -root |
awk -F'[ )] ' '/^KeyPress/ { a[NR 2] }
NR in a {
if ($8 == "Return") {
exit 0;
} else if ($8 == "BackSpace") {
system("truncate -s-1 timer.txt");
} else if (length($8) == 1) {
printf "%s", $8;
fflush(stdout);
}
system("pkill -RTMIN 3 dwmblocks");
}' | tee timer.txt
Сам таймер находится в строке состояния dwmblocks. Я хочу сначала назвать свои таймеры, а затем запустить его. Но я не думаю, что это так важно.
Цель этого скрипта — я хочу ввести символы в корневое окно DWM и мгновенно отобразить их в строке состояния. Итак, xev выдает информацию о нажатых клавишах, затем awk принимает эту информацию, находит точный ключ (из всей информации, которую выводит xev) и проверяет. Если ключ «Return», awk завершает работу (задание выполнено). Если ключом является «BackSpace», awk вызывает truncate из системы. Если это обычный символьный ключ, то awk выводит его в timer.txt с тройником (я мог бы использовать «> timer.txt «я думаю, тоже, но я хочу видеть выходные данные в моем терминале для отладки.
После каждого соответствующего нажатия клавиши (один символ) Я сбрасываю стандартный вывод. После всего этого я, наконец, вызываю pkill, чтобы dwmblocks знал, что он должен обновиться. (dwmblocks выдает операцию cat для файла)
Хорошо, «Возврат» и ввод символов работают нормально. Но есть проблема с «BackSpace». Я немного читал об этом (я бы сказал, что я все еще новичок в Unix, хотя я использую Linux уже два года), и я узнал, что запись в один и тот же файл из разных процессов — плохая новость. Тем не менее. Можно ли это как-то сделать? Дело в том, что truncate записывает в файл только тогда, когда awk этого не делает, так что, может быть, это было бы не так уж и важно?
Этот точный сценарий работал ранее вчера, но сейчас он не работает. Сначала я попытался использовать sed вместо truncate, и truncate, казалось, позволял мне удалять символы из timer.txt но теперь усечение, похоже, тоже больше не работает. Ну, это вроде как работает. Я могу вводить свои символы, а затем удалять их. НО.После нажатия Backspace я больше не могу вводить символы. Если я попытаюсь ввести символ, пробел тоже перестанет работать.
Так что да. У меня было бы несколько вопросов. Во-первых, в чем, черт возьми, проблема? Как я уже сказал, раньше это работало, а теперь нет. Я блуждаю в неопределенном поведении в этом скрипте?
Второе — можно ли это сделать — значение — могу ли я как-то записывать и удалять из того же файла. Может быть, с помощью какого-то другого инструмента, а не awk?
Заранее спасибо.
Ответ №1:
Вероятно, это не ответ, но это слишком много, чтобы указывать в комментарии. Я не знаю подробностей большинства инструментов, которые вы упоминаете, и я действительно не понимаю, что вы пытаетесь сделать, но:
Оболочка — это инструмент для управления файлами и процессами и планирования вызовов других инструментов. Awk — это инструмент для работы с текстом. Вы пытаетесь использовать awk как оболочку — у вас есть последовательность вызовов truncate
и pkill
и вызов system для создания подоболочки каждый раз, когда вы хотите выполнить любой из них. Что вы должны делать, например, просто:
shell { truncate }
но то, что вы на самом деле делаете, это:
shell { awk { system { shell { truncate } } } }
Можете ли вы забрать эту роль у awk и вернуть ее своей оболочке? Это должно сделать ваш общий сценарий проще, по крайней мере, концептуально и, вероятно, более надежным.
Может быть, попробуйте что-то вроде этого (непроверенный):
#!/usr/bin/env bash
while IFS= read -r str; do
case $str in
Return ) exit 0 ;;
BackSpace ) truncate -s-1 timer.txt ;;
? ) printf "%s" "$str" | tee -a timer.txt ;;
esac
pkill -RTMIN 3 dwmblocks
done < <(
xev -root |
awk -F'[ )] ' '/^KeyPress/{a[NR 2]} NR in a{print $8; fflush()}'
)
Я переместил запись в timer.txt внутри цикла, чтобы убедиться tee
, что вы не пытаетесь записать в него, пока вы его усекаете — в этом может и не быть необходимости.
Комментарии:
1. Ну, это не ответ на первый вопрос, но это ответ на второй! Да, это отлично работает, и спасибо, что указали на глубокое разделение truncate в shell в system в awk в shell. ДА. Я не думал об этом много, я думаю. Ваше решение отлично работает, похоже, и оно очень элегантное. Единственное, что мне осталось сделать, это перенести его на dash, поскольку я использую только dash в качестве своего интерпретатора по «забавным» причинам. Большое спасибо за помощь!
2. О, и последнее, что касается сценария. Это не работает так, как есть. Мне нужно было добавить
truncate -s0 timer.txt
перед циклом while и удалить-a
изtee -a timer.txt
команды, но я думаю, это только потому, что программное обеспечение в строке состояния, которое я использую, каким-то образом испортилось. Я имею в виду, что это работает, но dwmblocks неправильно отображает содержимое файла3. Окончательное решение получилось так:
#!/bin/sh truncate -s0 timer.txt xev -root | awk -F'[ )] ' '/^KeyPress/{a[NR 2]} NR in a{print $8; fflush()}' | while IFS= read -r str; do case $str in Return ) exit 0 ;; BackSpace ) truncate -s-1 timer.txt ;; ? ) printf "%s" "$str" | tee -a timer.txt ;; esac pkill -RTMIN 3 dwmblocks done