【问题标题】:Writing to the same file with awk and truncate使用 awk 和 truncate 写入同一个文件
【发布时间】:2020-10-15 15:30:18
【问题描述】:

我的系统是 Arch Linux,我的窗口管理器是 DWM。我使用 dash 作为我的 shell 解释器。

我已经为我的计时器编写了这个扩展 shell 脚本。


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 输出的所有信息中)并检查。如果键是“返回”,awk 退出(工作完成)。如果键是“BackSpace”,则 awk 从系统调用 truncate。如果它是常规字符键,则 awk 将其与 tee 一起输出到 timer.txt (我想我也可以使用 "> timer.txt" ,但我想在我的终端中查看输出以进行调试。

每次相关按键(单个字符)后,我都会刷新标准输出。毕竟我最终调用 pkill 以便 dwmblocks 知道它应该更新。 (dwmblocks 对文件发出 cat 操作)

好的,“返回”和字符输入工作正常。但是“BackSpace”存在问题。我已经读过一点(我想说我仍然是 Unix 新手,即使我已经使用 Linux 两年了),我发现从不同进程写入同一个文件是个坏消息。仍然。可以以某种方式完成吗?事实是 truncate 只在 awk 时写入文件,而不是,所以,也许这没什么大不了的?

这个确切的脚本昨天早些时候工作了,但现在不行了。起初,我尝试使用 sed 而不是 truncate 并且 truncate 似乎可以让我从 timer.txt 中删除字符,但现在 truncate 似乎不起作用也不再。嗯,它有点工作。我可以输入我的字符,然后我可以删除它们。 但是。 按 Backspace 后,我无法再输入任何字符。如果我尝试输入字符 Backspace 也会停止工作。

是的。我有几个问题。首先 - 到底是什么问题?正如我所说,它曾经有效,但现在无效。我是否在此脚本中徘徊在未定义的行为中?

第二 - 可以这样做吗 - 意思是 - 我可以以某种方式从同一个文件中写入和删除。也许使用其他工具,而不是 awk?

提前致谢。

【问题讨论】:

    标签: linux shell awk io pipe


    【解决方案1】:

    这可能不是答案,但评论太多了。我不知道您提到的大多数工具的详细信息,也不真正了解您要做什么,但是:

    shell 是一种工具,用于操作文件和进程并安排对其他工具的调用。 awk 是一个操作文本的工具。您正在尝试像 shell 一样使用 awk - 您让它对 truncatepkill 的调用进行排序,并在每次您想要执行其中任何一个时调用 system 来生成一个子 shell。例如,您应该做的只是:

    shell { truncate }
    

    但你实际上在做的是:

    shell { awk { system { shell { truncate } } } }
    

    你能把这个角色从 awk 中拿走,然后把它还给你的 shell 吗?它应该使您的整个脚本更简单,至少在概念上,并且可能更健壮。

    也许可以试试这样的东西(未经测试):

    #!/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 以确保 tees 在您截断它时不会尝试写入它 - 可能没有必要。

    【讨论】:

    • 嗯,这不是第一个问题的答案,而是第二个问题的答案!是的,这很好用,感谢您指出 truncate in shell in system in awk in shell 的深层层次。是的。我没有想那么多,我想。您的解决方案效果很好,看起来很优雅。我唯一需要做的就是将它移植到 dash,因为出于“有趣”的原因,我只使用 dash 作为我的解释器。非常感谢您的帮助!
    • 哦,关于脚本的最后一件事。它不能按原样工作。我需要在 while 循环之前添加 truncate -s0 timer.txt 并从 tee -a timer.txt 命令中删除 -a 但我认为这只是因为我正在使用的状态栏软件以某种方式搞砸了。我的意思是,它可以工作,但是 dwmblocks 不能正确显示文件内容
    • 最终解决方案是这样的:#!/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
    猜你喜欢
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 2019-09-05
    • 2018-11-23
    • 2016-05-14
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    相关资源
    最近更新 更多