Wednesday, October 27, 2021

[SOLVED] xargs and command substition issues

Issue

So, i have a horrifying oneliner, that gets a json value extracts data from it using jq and then iterates over the data with xargs. In the end it should produces some xml

legendary list-installed --json | jq '.[].title' | sed s/"\""/""/g | xargs -n1 -p -d "\n" -I {} echo '<button onclick="'$(legendary list-installed --json | jq 'map(if .title == "'{}'" then .app_name elif .title == "_" then "_" else "_" end)')'">'{}'</button>'

First of all, i didn't use the -r flag with jq because i thought the issue might be comming from it.

The expected output should be something like this:

<button onclick=app name e.g. Coley> The Escapist 2 </button>

So far that works (except for the app name part, as thats where it begins to get weird)

Because i hardly can debug with jq inside the command substition, i wanted to test what xargs gets passed into stdin via another command substition. I changed the part between the two button elements {} to $(echo {}), which still works. But by accident i piped it into wc -w, which outputs ONE if the content of the {} is actually TWO! image

❯ ./test.sh
<layout>
echo 'button onclick="[' '"_",' '"_",' '"_"' ']">Ape Out</button>' ?...^C⏎                                                                                                                                           
> cat test.sh
...'$(echo {})'</button>'

If i just echo the content of {} it returns this (i know that thats a useless echo, its just for consistency): second image When i count the words inside of my shell, and not inside of xargs

legendary list-installed --json | jq '.[].title' | head -n 1 | sed s/"\""/""/g | wc -w

it does return two.

Output of running with set -x

❯ ./test.sh
+ echo '<layout>'
<layout>
+ legendary list-installed --json
+ jq '.[].title'
+ sed 's/"//g'
++ legendary list-installed --json
++ jq 'map(if .title == "{}" then .app_name elif .title == "_" then "_" else "_" end)'
++ echo '{}'
++ tee lolsu,txt
+ xargs -n1 -p -d '\n' -I '{}' echo 'button onclick="[' '"_",' '"_",' '"_"' ']">{}</button>'
echo 'button onclick="[' '"_",' '"_",' '"_"' ']">Ape Out</button>' ?...

Seems like its not being substituted correctly... New problem is how i would make it replace the {} with the real one, inside of the substition.

TLDR: counting the value of xargs returns one, while the piped input should be two words (as i am iterating over line and not word) When running in shell it returns two (without using xargs, obv)


Solution

When you write

$(echo {} | wc -w)`

The input to the wc -w command is the string {}. That's one word, so the result is 1, and that gets substituted in place of $(echo {} | wc -w). It's not substituting anything from xargs there, because command substitutions are executed first and the results substituted into the command line.

So the argument that xargs receives contains this 1 already substituted.



Answered By - Barmar