-2

The following command works fine if launched in a console:

/bin/lshw -quiet -json -C network|/bin/jq '.[1] | .logicalname'

Please, note the 1 in square brackets.

On my computer this command delivers:

root@t15:/home/hmb# /bin/lshw -quiet -json -C network|/bin/jq '.[1] | .logicalname'
"wlp9s0"
root@t15:/home/hmb# 

When I tried to use this syntax within a bash script like this:

#!/bin/bash
# The next line works:
test1=$(/bin/lshw -quiet -json -C network|/bin/jq '.[1] | .logicalname')
/bin/echo "test1 = $test1"
i=1
# This line doesn't:
test2=$(/bin/lshw -quiet -json -C network|/bin/jq '.[$i] | .logicalname')
/bin/echo "test2 = $test2"

... things don't work as expected:

root@t15:/home/hmb/HPT/playground/hmbnetwatch# ./question.sh 
test1 = "wlp9s0"
jq: error: $i is not defined at <top-level>, line 1:
.[$i] | .logicalname  
jq: 1 compile error
test2 = 
root@t15:/home/hmb/HPT/playground/hmbnetwatch# 

I've tested all methods of escaping for the single quotes which occur in the jq command, but I wasn't able to use a script variable within these squared brackets.

I managed to find a way getting the needed information out of lshw by using an array, but out of curiosity and since I invested hours of trial and error without any success, I'd really like to know whether it is impossible to use a variable here or if there is a way.

It is said this is a duplicate of Passing bash variable to jq, but I don't think so. The question is why one should not inject a shell variable in the square brackets of jq, which is not really answered elsewhere.

E_net4
  • 27,810
  • 13
  • 101
  • 139
Rather Vi
  • 11
  • 6
  • Easy fix: `test2=$(/bin/lshw -quiet -json -C network|/bin/jq ".[$i] | .logicalname")`. This works because environment variables are substituted inside double quotes (but not inside single quotes). However it's recommended to use the method mentioned in the link in the above comment. – k314159 Mar 08 '22 at 12:13
  • @0stone0: ```jq -r --arg var "$var" '.[$var]'``` did not work. Thanks for the hint! – Rather Vi Mar 08 '22 at 12:19
  • 1
    You'll need an int, not a string. – 0stone0 Mar 08 '22 at 12:21

1 Answers1

0

No, don't inject shell variables into your jq filter! Rather use options provided by jq to introduce them as variables inside jq. In your case, when using a variable that holds a number, --argjson will do:

i=1
test2=$(/bin/lshw -quiet -json -C network|/bin/jq --argjson i $i '.[$i] | .logicalname')
pmf
  • 24,478
  • 2
  • 22
  • 31
  • I still wonder where's the difference between putting a [1] on the commandline and pasting in a variable like $i in the script. Why does the use of --argjson make such a difference? Thanks for taking your time to answer! – Rather Vi Mar 08 '22 at 13:28
  • Using `--arg` and `--argjson` ensures that a string you want to treat as a single value remains so, rather than just being string of characters that is subject to `jq` parsing it. It's the same principle as SQL injection. – chepner Mar 08 '22 at 13:49
  • `jq '.[1]'` and `jq --argjson var 1 '.[$var]'` have the same functionality and output, except that with the latter it is parametrized, i.e. you can easily change it by providing another value for that variable, including the provision of a value from a shell variable `x=1; jq --argjson var $x '.[$var]'`. Why this should be preferred over directly injecting `$x` is that it can fail predictably when something goes wrong. Suppose `$x` remained empty: `--argjson var $x` will detectably fail but `jq ".[$x]"` will just print all items because with `$x` being empty the actual filter just reads `.[]`. – pmf Mar 08 '22 at 13:50