Search code examples
bashadbjq

Bash / JQ - cant assign pull string and assign variable


I am trying to write a script that will allow me to pull a command from a json file and use it in the script

Here is the data.json

"play_music": {
            "name": "play_music",
            "command" : "adb shell am start -a android.intent.action.VIEW -d file:///${device_directory}${music_file} -t audio/${audio_type}",
            "variables" : {
                "device_directory" : {
                    "name" : "device_directory",
                    "variable" : "/Downloads/"
                },
                "music_file" : {
                    "name" : "music_file",
                    "variable" : "test.mp3"
                },
                "audio_type" : {
                    "name" : "audio_type",
                    "variable" : "mp3"
                }
            }

And here is a snippet of code that I am trying to get to run the command

while true; do
    variables=( $(jq -r ".commands.$option.variables | map(.name) | .[]" data.json))
    ##echo ${variables[@]}
    
    for i in ${variables[@]}; do
        ##echo -e "\n$i"
        var=$(jq -r ".commands.$option.variables.$i.variable" data.json)
        if [[ -z $var ]]; then
            ##echo -e "var is empty"
            echo -e "Enter $i: "
            read $i
        else
            ##echo -e "var is full"
            declare $i=$var
        fi
    done

echo -e "\nmusic file='${music_file}'"
echo -e "\naudio type='${audio_type}'"
echo ""

command="$(jq -r ".commands.$option.command" data.json)"
echo $command

The variables report as

music file='test.mp3'

audio type='mp3'

The command just comes back as: adb shell am start -a android.intent.action.VIEW -d file:///${device_directory}${music_file} -t audio/${audio_type} But what I am looking for is adb shell am start -a android.intent.action.VIEW -d file:////Downloads/test.mp3 -t audio/mp3

I tried running outputting the command in different forms $(command), "$command", etc but have had no luck.


Solution

  • This is not very elegant (see More Elegant below), and I'm confident you'll get a better answer, but it's possible for jq to form the command string in one go using a filter like this. Perhaps you can adapt it for your application.

    .commands.play_music
    | .command as $cmd
    | .variables
      | .device_directory.variable as $dir
      | .music_file.variable as $file
      | .audio_type.variable as $type
    | $cmd
    | sub("\\${device_directory}"; $dir)
    | sub("\\${music_file}"; $file)
    | sub("\\${audio_type}"; $type)
    

    Output using your probable input:

    "adb shell am start -a android.intent.action.VIEW -d file:////Downloads/test.mp3 -t audio/mp3"
    

    Try it on jqplay.org.

    More Elegant

    The same output is produced by this filter and is perhaps more adaptable to your application.

    .commands.play_music
    | (.variables|map({k: .name, v: .variable})) as $vars
    | reduce $vars[] as $var (.command; .|sub("\\${\($var.k)}"; $var.v))
    

    Try it on jqplay.org.

    ... and more incomprehensibly as:

    .commands.play_music
    | reduce .variables[] as $var (.command; .|sub("\\${\($var.name)}"; $var.variable))
    

    ... still outputs:

    "adb shell am start -a android.intent.action.VIEW -d file:////Downloads/test.mp3 -t audio/mp3"
    

    Try this on jqplay.org.