Bash

每次匹配正則表達式時執行命令,從沒有 EOF 的標準輸入讀取

  • November 15, 2021

我正在嘗試編寫一個 BASH 腳本,該腳本每次在 dbus-monitor 的輸出中找到某個字元串(稍後在文章中指定的參數)時創建一個時間戳(要寫入文件)。我的腳本的主要目的是在歌曲開始在 Spotify 上播放時保存時間(包括毫秒)和日期,因為它利用了通知。

string "Spotify"每當播放歌曲時,都會輸出以下命令。

dbus-monitor --session interface='org.freedesktop.Notifications',member='Notify' | grep 'string "Spotify"'

我的嘗試:

search='string "Spotify"'
found=$(dbus-monitor --session interface='org.freedesktop.Notifications',member='Notify' | grep 'string "Spotify"')

while [ ${search} == ${found} ]; do
   date -u +%Y%M%d-%H%M%S.%N >> timestamp.txt
done

我假設我的程式碼功能障礙的原因是 dbus-monitor 連續執行,因此阻止了 while 循環執行。

使用awk代替grep- 類似:

dbus-monitor ... | awk '/Spotify/ {
   system("date -u +%Y%m%d-%H%M%S.%N >> timestamp.txt")
 }'

(注意使用%Y%m%d代替%Y%M%D- 大寫 - M 是分鐘,而不是月。大寫 - D 相當於%m/%d/%y

這將使用 awk 的system()函式在輸入中看到“Spotify”時在子 shell 中執行 date 命令。或者,使用 awk 的內置日期格式和重定向:

dbus-monitor ... | awk '/Spotify/ {
   print strftime("%Y%m%d-%H%M%S") >> "timestamp.txt"
 }'

此版本不會在時間戳中列印納秒,因為strftime()不支持%N.

或者,使用 perl 而不是 awk。這將允許您使用 perl 的Desktop::Notify模組來獲取通知或Net::DBus直接與 dbus 通信。

由於您有 GNU 實用程序,因此您可以執行以下操作:

dbus-monitor --session interface='org.freedesktop.Notifications',member='Notify' |
 sed -un 's/^.*string "Spotify".*$/now/p' |
 stdbuf -oL date -uf - +%Y%m%d-%H%M%S.%N >> timestamp.txt

dbus-monitor已經禁用了緩衝,所以stdbuf -oL那裡沒有必要。

-uGNU的選項sed會禁用輸出緩衝,並使其在此處不可搜尋時一次讀取一個字節的輸入。我們不需要後者,但我們需要前者,以便它一讀取就輸出一行。

在這裡,sed每次now找到包含string "Spotify".

now是餵給date. 使用-f -,從標準輸入date讀取date要列印的。對於now它讀取的每一個,它以指定的格式列印目前時間。stdbuf -oL我們確保輸出立即而不是分塊發送到文件timestamp.txt中。

如果您確實想執行任何任意命令,而不是僅使用 zsh/bash/ksh93 輸出目前時間,您可以執行以下操作:

while IFS= read -ru3 line || [ -n "$line" ]; do
 any arbitrary command
done 3< <(
 dbus-monitor --session interface='org.freedesktop.Notifications',member='Notify' |
 grep --line-buffered 'string "Spotify"'
)

引用自:https://unix.stackexchange.com/questions/677427