Linux bash スクリプトのコマンドの結果を解析する

Linux bash スクリプトのコマンドの結果を解析する

Pulseaudio の入力/出力を制御するために、Linux で小さな bash スクリプトを作成しています。

私がやりたいのは、1 つを除くすべてのシンク入力をいずれかのシンクにルーティングすることです。ルーティングしたくないシンク入力は次のものです:

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

bash スクリプト内のコマンドの結果の各「ブロック」(つまり、各シンク入力) で 1 つだけではなく 2 つの要素を読み取ることができるように、結果を解析するにはどうすればよいでしょうpactl list sink-inputsか。

答え1

問題は、doループ内で設定された変数が、あるパスから次のパスにかけて維持されないことです。

簡単な解決策は、リスト出力を一時ファイルに保存し、それを 2 回スキャンすることです。

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 

これで、必要な2つの変数が揃いました。これはあまりエレガントな解決策ではありませんが、わかりやすく、維持しやすいものです。関数を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

コンセプト:

  1. pactl …ローカライズされた出力を回避するには、set を指定して呼び出しますLC_ALL=C(これは明らかに必要ありませんが、一般的には必要です)。
  2. 関係のない行を破棄するために使用しますegrep。次のような行のペアを取得します。

    Sink Input #12345
            module-stream-restore.id = "whatever"
    
  3. 行がペアになっていると仮定して、適切な(ペアの最初の行には 、 2 番目の行には ) を使用readして 2 つずつ抽出し、関連するデータを抽出します。必要のない部分にはダミー変数を使用します。IFS#=

  4. 抽出されたデータで作業します。IFS='='ペアの 2 行目では、 の後のすべて、つまり隣接するスペースと二重引用符も抽出されることに注意してください。これが、コード (下記) でだけでなく と=一致させる理由です。' "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

関連情報