我正在 Linux 中編寫一個小型 bash 腳本來控制 Pulseaudio 輸入/輸出。
我想要做的是將除一個之外的所有接收器輸入路由到一個或另一個接收器。我不想路由的接收器輸入是這個:
pi@raspberrypi:~/fonction $ pactl list sink-inputs
Sink Input #36062
Driver: protocol-native.c
Owner Module: 2
Client: 198
Sink: 2
Sample Specification: s16le 2ch 44100Hz
Channel Map: front-left,front-right
Format: pcm, format.sample_format = "\"s16le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
Corked: no
Mute: no
Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB
balance 0.00
Buffer Latency: 120000 usec
Sink Latency: 236349 usec
Resample method: n/a
Properties:
media.name = "ALSA Playback"
application.name = "ALSA plug-in [snapclient]"
native-protocol.peer = "UNIX socket client"
native-protocol.version = "32"
application.process.id = "539"
application.process.user = "snapclient"
application.process.host = "raspberrypi"
application.process.binary = "snapclient"
application.language = "C"
application.process.machine_id = "69e523231bb44f2e926a758a63cbb5b1"
module-stream-restore.id = "sink-input-by-application-name:ALSA plug-in [snapclient]"
要將接收器輸入移動到另一個接收器,我需要使用以下命令:
pactl move-sink-input <sink-input id> <sink id>
因此,我必須解析第一個命令的結果才能取得Sink Input #ID
(包含在第一行中),但我必須使用條件來檢查module-stream-restore.id
(最後一行)是否真的是我想要路由的內容。
我需要類似的東西:
if [ <last line> = 'sink-input name I need' ]
then
pactl move-sink-input <id from first line> <my sink name>
fi
我只是不知道如何解析命令來獲取這兩個資訊。
現在,我只能取得最後一行,或只能取得 #ID。
pactl list short sink-inputs|while read stream; do
streamId=$(echo $stream )
id=$(echo pactl list sink-inputs | grep module-stream-restore.id | grep -o '".*"' |sed 's/"//g')
if [ "$streamId" != 'sink-input-by-application-name:ALSA plug-in [snapclient]' ]
then
pactl move-sink-input "$streamId" Snapcast
fi
我如何繼續解析結果,pactl list sink-inputs
以便我可以在 bash 腳本中該命令結果的每個「區塊」(也稱為每個接收器輸入)中讀取兩個元素,而不僅僅是一個元素?
答案1
問題在於do
循環中設定的變數不會從一次傳遞到下一次維護。
簡單的解決方案是將列表輸出保存到臨時文件,然後掃描兩次:-
pactl list short sink-inputs >temp.lst
streamId=$(sed -n 's/^Sink Input #\(.*\)$/\1/p' <temp.lst)
id=$(sed -n 's/.*module-stream-restore.id = "\(.*\)"$/\1/p' <temp.lst)
rm temp.lst
現在您已經設定了兩個需要的變數。這不是一個非常優雅的解決方案,但它很簡單且易於理解和維護。請注意,我曾經用來sed
執行這些grep
功能:有關其工作原理的指南,請參閱這裡。
但是,您可以使用更複雜的解決方案來避免臨時檔案:-
Ids="$(pactl list short sink-inputs | \
sed -n -e 's/^Sink Input #\(.*\)$/\1/p' \
-e 's/.*module-stream-restore.id = "\(.*\)"$/\1/p')"
現在,您在 中擁有兩個變量Ids
,並用換行符分隔:您可以使用以下命令拆分它們:-
streamId="${Ids%$'\n'*}"
Id="${Ids#*$'\n'}"
Id
如果你也不喜歡臨時變量,你可以使用Ids
!
我已經通過複製列表輸出並使用它而不是真正的pactl
命令來測試了這一點,因此它應該全部與實時命令一起使用。
答案2
這個概念:
pactl …
使用set呼叫LC_ALL=C
以避免本地化輸出(您顯然不需要這個,但一般情況下人們需要)。用於
egrep
丟棄不相關的行。您想要獲得如下所示的線對:Sink Input #12345 module-stream-restore.id = "whatever"
假設這些行成對出現,
read
它們兩兩用適當的IFS
(#
對於一對中的第一行,=
對於第二行)來提取相關資料。對不需要的部分使用虛擬變數。- 使用提取的數據。請注意,
IFS='='
對於一對中的第二行,將提取 後的所有內容=
,即相鄰的空格和雙引號。這就是為什麼在程式碼(下面)中我匹配' "sink-… [snapclient]"'
,而不僅僅是'sink-… [snapclient]'
。
代碼:
LC_ALL=C pactl list sink-inputs |
egrep "^Sink Input #|module-stream-restore.id = " |
while IFS='#' read dummy id && IFS='=' read dummy restid; do
if [ "$restid" = ' "sink-input-by-application-name:ALSA plug-in [snapclient]"' ] ; then
printf 'Match for ID %s.\n' "$id"
# do something
else
printf 'No match for ID %s.\n' "$id"
# do something else
fi
done