Анализ результата команды в скрипте Linux bash

Анализ результата команды в скрипте Linux bash

Я пишу небольшой bash-скрипт в Linux для управления входами/выходами 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

Концепт:

  1. Вызовите pactl …с помощью LC_ALL=Cset, чтобы избежать локализованного вывода (вам это, по-видимому, не нужно, но в общем случае людям нужно).
  2. Используйте egrepдля отбрасывания ненужных строк. Вы хотите получить пары строк, которые выглядят так:

    Sink Input #12345
            module-stream-restore.id = "whatever"
    
  3. Предполагая, что строки идут парами, readпо две с соответствующим IFS( #для первой строки в паре, =для второй) для извлечения соответствующих данных. Используйте фиктивные переменные для частей, которые вам не нужны.

  4. Работа с извлеченными данными. Обратите внимание, что 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

Связанный контент