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

697
Gob Tron

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

Was ich tun möchte, ist, alle Sink-Eingänge mit Ausnahme eines in die eine oder andere Senke zu routen. Der Sink-Input, den ich nicht routen 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 eine Sink-Eingabe in eine andere Senke zu verschieben, muss ich diesen Befehl verwenden:

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

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

Ich würde so etwas brauchen:

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 erhalten.

 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 vor, um das Ergebnis zu analysieren, pactl list sink-inputsdamit ich in jedem "Block" (auch als "sink-input") des Ergebnisses dieses Befehls in einem Bash-Skript zwei Elemente lesen kann, nicht nur ein Element?

1
Warum speichern Sie die Sink-Eingangsnummer nicht in einer Variablen? Nifle vor 6 Jahren 0
Die erste Zeile und die letzte Zeile erhalten Sie mit `head -n1` und` tail -n1` oder `sed '1! D'` und` sed' $! D'` Paulo vor 6 Jahren 0
@Nifle, die Sink-Input-Nummer kann sich jederzeit ändern. Gob Tron vor 6 Jahren 0
@Paulo, ich werde es heute Abend versuchen. Es könnte genau das sein, was ich brauche. Gob Tron vor 6 Jahren 0

2 Antworten auf die Frage

2
AFH

Das Problem ist, dass in der doSchleife festgelegte Variablen nicht von einem Durchgang zum nächsten beibehalten werden.

Die einfache Lösung besteht darin, die Listenausgabe in einer temporären Datei zu speichern und anschließend 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 jetzt die beiden Variablen, die Sie beide benötigen. Es ist keine sehr elegante Lösung, aber es ist unkompliziert und leicht zu verstehen und zu warten. Beachten Sie, dass ich seddie grepFunktionen ausgeführt habe: Eine Anleitung, wie dies funktioniert, finden Sie hier .

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 Ids, getrennt durch eine neue Zeile: Sie können sie mit teilen: -

streamId="$" Id="$" 

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

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

2
Kamil Maciorowski

Das Konzept:

  1. Rufen Sie pactl …mit LC_ALL=Cset auf, um eine lokalisierte Ausgabe zu vermeiden (Sie brauchen dies offensichtlich nicht, aber im Allgemeinen ist dies der Fall).
  2. Verwenden Sie egrepauf irrelevante Linien zu verwerfen. Sie möchten Zeilenpaare erhalten, die folgendermaßen aussehen:

    Sink Input #12345 module-stream-restore.id = "whatever" 
  3. Unter der Annahme, dass die Zeilen paarweise verlaufen, werden readsie zwei Mal mit "richtig" IFS( #für die erste Zeile eines Paares, =für die zweite Zeile ) verwendet, um relevante Daten zu extrahieren. Verwenden Sie Dummy-Variablen für Teile, die Sie nicht benötigen.

  4. Mit extrahierten Daten arbeiten Beachten Sie, dass IFS='='für die zweite Zeile in einem Paar alles nach =, dh das angrenzende Leerzeichen und doppelte Anführungszeichen, extrahiert wird . Deshalb spiele ich im Code (unten) ' "sink-… [snapclient]"'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