Analysieren des Ergebnisses eines Befehls in einem Linux-Bash-Skript

Analysieren des Ergebnisses eines Befehls in einem Linux-Bash-Skript

Ich schreibe ein kleines Bash-Skript in Linux, um Pulseaudio-Ein-/Ausgänge zu steuern.

Ich möchte alle Sink-Eingänge, bis auf einen, in den einen oder anderen Sink leiten. Der Sink-Eingang, den ich nicht leiten möchte, ist dieser:

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]"

Um einen Sink-Eingang zu einem anderen Sink zu verschieben, muss ich diesen Befehl verwenden:

pactl move-sink-input <sink-input id> <sink id>

Ich muss also das Ergebnis des ersten Befehls analysieren, um das Sink Input #ID(in der ersten Zeile enthaltene) zu erhalten, aber ich muss eine Bedingung verwenden, um zu prüfen, ob module-stream-restore.id(letzte Zeile) wirklich das ist, was ich weiterleiten möchte.

Ich bräuchte sowas wie:

if [ <last line> = 'sink-input name I need' ] 
then
  pactl move-sink-input <id from first line> <my sink name>
fi

Ich weiß einfach nicht, wie ich den Befehl analysieren soll, um BEIDE Informationen zu erhalten.

Im Moment kann ich nur die letzte Zeile oder nur die #ID abrufen.

 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

Wie gehe ich beim Analysieren des Ergebnisses vor, pactl list sink-inputssodass ich in jedem „Block“ (auch bekannt als jeder Sink-Eingabe) des Ergebnisses dieses Befehls in einem Bash-Skript zwei Elemente und nicht nur eines lesen kann?

Antwort1

Das Problem besteht darin, dass die in der doSchleife festgelegten Variablen nicht von einem Durchlauf zum nächsten beibehalten werden.

Die einfache Lösung besteht darin, die Listenausgabe in einer temporären Datei zu speichern und sie dann zweimal zu scannen:-

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 

Sie haben nun die beiden Variablen, die Sie benötigen, beide gesetzt. Es ist keine sehr elegante Lösung, aber sie ist unkompliziert und leicht zu verstehen und zu warten. Beachten Sie, dass ich sedzur Ausführung der grepFunktionen verwendet habe: Eine Anleitung zur Funktionsweise finden Sie unterHier.

Sie können die temporäre Datei jedoch mit einer komplizierteren Lösung vermeiden: -

Ids="$(pactl list short sink-inputs | \
       sed -n -e 's/^Sink Input #\(.*\)$/\1/p' \
              -e 's/.*module-stream-restore.id = "\(.*\)"$/\1/p')"

Sie haben jetzt beide Variablen in Ids, getrennt durch eine neue Zeile: Sie können sie wie folgt trennen: -

streamId="${Ids%$'\n'*}"
Id="${Ids#*$'\n'}"

Wenn Sie auch keine temporären Variablen mögen, können Sie Idanstelle von verwenden Ids!

Ich habe dies getestet, indem ich Ihre Listenausgabe kopiert und diese anstelle eines echten pactlBefehls verwendet habe. Daher sollte alles mit einem Live-Befehl funktionieren.

Antwort2

Das Konzept:

  1. Rufen Sie es pactl …mit LC_ALL=C„Set“ auf, um eine lokalisierte Ausgabe zu vermeiden (Sie brauchen das offensichtlich nicht, aber im Allgemeinen ist es das).
  2. Verwenden Sie egrepdiese Option, um irrelevante Zeilen zu verwerfen. Sie möchten Zeilenpaare erhalten, die wie folgt aussehen:

    Sink Input #12345
            module-stream-restore.id = "whatever"
    
  3. Angenommen, die Zeilen verlaufen paarweise, readdann verwenden Sie jeweils zwei mit der richtigen Reihenfolge IFS( #für die erste Zeile in einem Paar, =für die zweite), um relevante Daten zu extrahieren. Verwenden Sie Dummyvariablen für Teile, die Sie nicht benötigen.

  4. Arbeiten Sie mit extrahierten Daten. Beachten Sie, dass IFS='='für die zweite Zeile in einem Paar alles nach dem extrahiert wird =, also auch das angrenzende Leerzeichen und die Anführungszeichen. Aus diesem Grund vergleiche ich im Code (unten) mit ' "sink-… [snapclient]"'und nicht nur mit 'sink-… [snapclient]'.

Der Code:

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

verwandte Informationen