Analisando o resultado de um comando em um script bash do Linux

Analisando o resultado de um comando em um script bash do Linux

Estou escrevendo um pequeno script bash no Linux para controlar entradas/saídas do Pulseaudio.

O que eu quero fazer é rotear todas as entradas do coletor, exceto uma, para um ou outro coletor. A entrada do coletor que não quero rotear é esta:

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

Para mover uma entrada de coletor para outro coletor, preciso usar este comando:

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

Portanto, tenho que analisar o resultado do primeiro comando para obter o Sink Input #ID(contido na primeira linha), mas tenho que usar uma condição para verificar se module-stream-restore.id(última linha) é realmente o que desejo rotear.

Eu precisaria de algo como:

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

Só não sei como analisar o comando para obter AMBAS as informações.

No momento, consigo obter apenas a última linha ou apenas o #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

Como procedo para analisar o resultado pactl list sink-inputspara poder ler dois elementos, não apenas um, em cada "bloco" (também conhecido como entrada de coletor) do resultado desse comando em um script bash?

Responder1

O problema é que as variáveis ​​definidas no doloop não são mantidas de uma passagem para a próxima.

A solução simples é salvar a saída da lista em um arquivo temporário e depois digitalizá-la duas vezes: –

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 

Agora você tem as duas variáveis ​​que precisa definir. Não é uma solução muito elegante, mas é simples e fácil de entender e manter. Observe que eu costumava sedexecutar as grepfunções: para obter um guia de como isso funciona, consulteaqui.

No entanto, você pode evitar o arquivo temporário com uma solução mais complicada: –

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

Agora você tem ambas as variáveis Ids​​separadas por uma nova linha: você pode dividi-las com: -

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

Se você também não gosta de variáveis ​​temporárias, pode usar Idem vez de Ids!

Eu testei isso copiando a saída da sua lista e usando isso em vez de um pactlcomando real, então tudo deve funcionar com um comando ativo.

Responder2

O conceito:

  1. Invoque pactl …com LC_ALL=Cset para evitar saída localizada (aparentemente você não precisa disso, mas em geral as pessoas precisam).
  2. Use egreppara descartar linhas irrelevantes. Você deseja obter pares de linhas assim:

    Sink Input #12345
            module-stream-restore.id = "whatever"
    
  3. Supondo que as linhas vão aos pares, readfaça-as duas a duas com o devido IFS( #para a primeira linha do par, =para a segunda) para extrair dados relevantes. Use variáveis ​​fictícias para peças desnecessárias.

  4. Trabalhe com dados extraídos. Observe que IFS='='para a segunda linha de um par extrairá tudo após o =, ou seja, o espaço adjacente e as aspas duplas também. É por isso que no código (abaixo) eu comparo contra ' "sink-… [snapclient]"', não apenas 'sink-… [snapclient]'.

O código:

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

informação relacionada