Working nested quotes in Bash?


  • 🚽 Regular

    Is anyone a Bash expert, trying to run a command with nested single and double quotes is driving me insane.

    I have this command, it runs fine on a line by itself in bash.bashrc:

    runuser -l pi -c "sudo -E ionice -c 1 -n 0 nice -n -18 `which gst-launch-1.0` -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index.mpd name=source ! application/dash+xml ! dashdemux name=dd0 ! qtdemux name=d1 ! h264parse ! omxh264dec ! glupload ! glcolorconvert ! glcolorscale ! capsfilter caps='video/x-raw(memory:GLMemory),width=800,height=480' ! glimagesink"
    

    I want to stick it in this loop as the variable run_command:

    until $run_command; do
        echo "Server '$run_command' crashed with exit code $?.  Respawning.." >&2
        sleep 10
    done
    

    I though I could simply use the shell to build a string and get the quotes correct but I cannot get it working:

    printf -v gstreamer_command '%q ' sudo -E ionice -c 1 -n 0 nice -n -18 `which gst-launch-1.0` -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index.mpd name=source ! application/dash+xml ! dashdemux name=dd0 ! qtdemux name=d1 ! h264parse ! omxh264dec ! glupload ! glcolorconvert ! glcolorscale ! capsfilter caps='video/x-raw(memory:GLMemory),width=800,height=480' ! glimagesink
    printf -v run_command '%q ' runuser -l pi -c $gstreamer_command
    

    But I get this instead:

    Server 'runuser -l pi -c sudo -E ionice -c 1 -n 0 nice -n -18 /usr/local/bin/gst -launch-1.0 -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index. mpd name=source \! application/dash+xml \! dashdemux name=dd0 \! qtdemux name=d1 \! h264parse \! omxh264dec \! glupload \! glcolorconvert \! glc olorscale \! capsfilter caps=video/x-raw\(memory:GLMemory\)\,width=800\ ,height=480 \! glimagesink ' crashed with exit code 1. Respawning..
    runuser: invalid option -- 'E'

    I can see the problem, runuser thinks the entire command for it to run is simply sudo and the rest is arguments, but I'm not sure why it's happening :/


  • Banned

    Try Python.


  • 🚽 Regular

    @gąska said in Working nested quotes in Bash?:

    Try Python.

    Unfortunately that means learning Python. I'll take a look.


  • Banned

    @cursorkeys learning entirety of Python will take you less time than this single piece of Bash.



  • Would a function work?

    Edit: I just noticed that the inner loop prints the command. Some answers here might help with that.

    run_command()
    {
        runuser -l pi -c "sudo -E ionice -c 1 -n 0 nice -n -18 `which gst-launch-1.0` -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index.mpd name=source ! application/dash+xml ! dashdemux name=dd0 ! qtdemux name=d1 ! h264parse ! omxh264dec ! glupload ! glcolorconvert ! glcolorscale ! capsfilter caps='video/x-raw(memory:GLMemory),width=800,height=480' ! glimagesink"
    }
    
    until run_command; do
        echo "Server '$run_command' crashed with exit code $?.  Respawning.." >&2
        sleep 10
    done


  • @cursorkeys Is using printf a requirement? I.e., the following seems to work:

    gstreamer_command="sudo -E ionice -c 1 -n 0 nice -n -18 `which gst-launch-1.0` -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index.mpd name=source ! application/dash+xml ! dashdemux name=dd0 ! qtdemux name=d1 ! h264parse ! omxh264dec ! glupload ! glcolorconvert ! glcolorscale ! capsfilter caps='video/x-raw(memory:GLMemory),width=800,height=480' ! glimagesink"
    run_command="runuser -l pi -c \"$gstreamer_command\""
    
    ...
    
    until eval $run_command; do
        ...
    done
    

    (Actually, you can probably do it with printf+eval too...)



  • Building quoted command lines, while possible, is hard to do properly. Better keep the command line as an array. It's possible, since you're using bash:

    declare -a run_command=(runuser -l pi -c "sudo -E ionice -c 1 -n 0 nice -n -18 `which gst-launch-1.0` -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index.mpd name=source ! application/dash+xml ! dashdemux name=dd0 ! qtdemux name=d1 ! h264parse ! omxh264dec ! glupload ! glcolorconvert ! glcolorscale ! capsfilter caps='video/x-raw(memory:GLMemory),width=800,height=480' ! glimagesink")
    
    # ...
    
    until "${run_command[@]}"; do
        echo "Server '${run_command[*]}' crashed with exit code $?.  Respawning.." >&2
        sleep 10
    done
    

    ${array[@]} is like $@, but for bash arrays instead of command like arguments: it expands each element to a separate word, and the outside quotes ensure that each word is quoted.

    ${array[*]} is, on the other hand, like $*: it glues the array elements together when quoted.

    Yes, the syntax looks crazy.


  • 🚽 Regular

    @mzh said in Working nested quotes in Bash?:

    Would a function work?

    Edit: I just noticed that the inner loop prints the command. Some answers here might help with that.

    run_command()
    {
        runuser -l pi -c "sudo -E ionice -c 1 -n 0 nice -n -18 `which gst-launch-1.0` -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index.mpd name=source ! application/dash+xml ! dashdemux name=dd0 ! qtdemux name=d1 ! h264parse ! omxh264dec ! glupload ! glcolorconvert ! glcolorscale ! capsfilter caps='video/x-raw(memory:GLMemory),width=800,height=480' ! glimagesink"
    }
    
    until run_command; do
        echo "Server '$run_command' crashed with exit code $?.  Respawning.." >&2
        sleep 10
    done
    

    @cvi said in Working nested quotes in Bash?:

    @cursorkeys Is using printf a requirement? I.e., the following seems to work:

    gstreamer_command="sudo -E ionice -c 1 -n 0 nice -n -18 `which gst-launch-1.0` -v curlhttpsrc location=http://192.168.158.106/dash/test_dash/index.mpd name=source ! application/dash+xml ! dashdemux name=dd0 ! qtdemux name=d1 ! h264parse ! omxh264dec ! glupload ! glcolorconvert ! glcolorscale ! capsfilter caps='video/x-raw(memory:GLMemory),width=800,height=480' ! glimagesink"
    run_command="runuser -l pi -c \"$gstreamer_command\""
    
    ...
    
    until eval $run_command; do
        ...
    done
    

    (Actually, you can probably do it with printf+eval too...)

    Both of these work great, thank you!

    I'll look into Python as well when I get more time, seems quite neat.


  • Considered Harmful

    Try PowerShell.


Log in to reply