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

Я пишу небольшой сценарий 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?

2 ответа

Концепт:

  1. взывать pactl … с LC_ALL=C установите, чтобы избежать локализованного вывода (вам, видимо, это не нужно, но в общем случае люди это делают).
  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

Проблема в том, что переменные, установленные в 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 команда, так что все должно работать с живой командой.

Другие вопросы по тегам