2

I have a script where I dinamically change the arguments which must be passed to a command (mkvpropedit in this case). Consider the example script below:

#!/bin/bash

LANG_NAME="eng lish"

MYSETTINGS=()
MYSETTINGS+=("--edit 1")
MYSETTINGS+=("--set \"language=${LANG_NAME}\"")

echo "Count = ${#MYSETTINGS[@]}" # should be 2
set -x # enable to see the invoked command
mkvpropedit ${MYSETTINGS[@]}

When I run this, I get in the console:

[~] # ./test.sh
Count = 2
+ mkvpropedit --edit 1 --set '"language=eng' 'lish"'

But I would like not having the single quotes on the final invocation of mkvpropedit, like so:

+ mkvpropedit --edit 1 --set "language=eng lish"

I tried also echoing the array into a variable, and echo removes the single quote, but then I'm not able to use the variable as an argument of mkvpropedit because the single quotes appear again...

Of course the script has to work also if the variable is a single word, such as LANG_NAME="eng" . My Bash version is 3.2 (Busybox, actually).

Updated question

Probably the example below better explains what I'm trying to do. I've changed some names to be replicable.

#!/bin/bash

TITLE_NAME="my title"

MYSETTINGS=()
MYSETTINGS+=("--edit track:2")
MYSETTINGS+=("--set \"name=${TITLE_NAME}\"")

set -x
mkvpropedit file.mkv ${MYSETTINGS[@]} 

If I run this script, I get (due to the wrong quote):

# ./test.sh
+ mkvpropedit file.mkv --edit track:2 --set '"name=my' 'title"'
Error: More than one file name has been given ('file.mkv' and 'title"').

While if I run, manually:

# mkvpropedit file.mkv --edit track:2 --set "name=my title"
The file is being analyzed.
The changes are written to the file.
Done.

So it's definitely a quoting issue; I would like to invoke mkvpropedit using the array in the script.

Using eval

What seems to work, at the moment, is inserting mkvpropedit and file.mkv into the array and eventually call eval "${MYSETTINGS[@]}", but is it worth and safe? Isn't eval evil (pun intended)?

TITLE_NAME="my title"

MYSETTINGS=(mkvpropedit file.mkv)
MYSETTINGS+=("--edit track:2")
MYSETTINGS+=("--set \"name=${TITLE_NAME}\"")

set -x
eval "${MYSETTINGS[@]}"

Returns:

# ./test.sh
+ eval mkvpropedit file.mkv '--edit track:2' '--set "name=my title"'
++ mkvpropedit file.mkv --edit track:2 --set 'name=my title'
The file is being analyzed.
The changes are written to the file.
Done.
1
  • no-break space echo -n my title | xxd (e280 af)
    – nezabudka
    Aug 12, 2021 at 17:39

2 Answers 2

5

There are no single quotes - that's just the shell's unambiguous representation of the variable's contents when you use set -x. You can see that if you instead look at the array elements using declare -p or by printing them one at a time:

LANG_NAME="eng lish"

MYSETTINGS=()
MYSETTINGS+=("--edit 1")
MYSETTINGS+=("--set \"language=${LANG_NAME}\"")

then

$ declare -p MYSETTINGS
declare -a MYSETTINGS=([0]="--edit 1" [1]="--set \"language=eng lish\"")

or

$ printf '>>>%s<<<\n' "${MYSETTINGS[@]}"
>>>--edit 1<<<
>>>--set "language=eng lish"<<<

However, you almost certainly want to pass --edit, 1, --set, and language=eng lish as separate tokens to the command, which means

  1. quoting each token that contains whitespace or glob characters during array construction, like language="${LANG_NAME}" or "language=${LANG_NAME}"

  2. double quoting the array expansion when you use it (to prevent word-splitting and filename generation - aka "split+glob")

So

LANG_NAME="eng lish"

MYSETTINGS=()
MYSETTINGS+=(--edit 1)
MYSETTINGS+=(--set language="${LANG_NAME}")

then

mkvpropedit file.mkv "${MYSETTINGS[@]}"

Note that you do not need additional double quotes around the variable expansion, because double-quoted "${name[@]}" expands each element of name to a separate word without further tokenization - further quotes like \"name=${TITLE_NAME}\" would be passed to the command literally.

See also How can we run a command stored in a variable?

14
  • Mm, that's not clear. If I add the double quotes to the final statement, i.e. mkvpropedit "${MYSETTINGS[@]}" what I get with set -x is + mkvpropedit '--edit 1' '--set "language=eng lish"'. I know it's just a representation, but the thing is I need to pass the --set "language=eng lish" text as an argument to mkvpropedit and there the single quotes are added, because mkvpropedit complains.
    – virtualdj
    Aug 12, 2021 at 13:27
  • @virtualdj are you sure it's not complaining about the fact that you passed --set language=eng lish as a single element?` Have you tried MYSETTINGS+=(--set language="\"${LANG_NAME}\"") Aug 12, 2021 at 13:33
  • I've added another example below, to better clarify. And I need language to be quoted as well, i.e. --set "language=eng lish" or --set "name=my title" (with the new example).
    – virtualdj
    Aug 12, 2021 at 13:42
  • The link is more useful, but doesn't explain how to deal with the double quotes inside a single element, i.e. --set "name=my title". So your answer doens't work, at the moment.
    – virtualdj
    Aug 12, 2021 at 14:05
  • It's unlikely that they want the literal double quotes around $LANG_NAME.
    – Kusalananda
    Aug 12, 2021 at 14:11
-2

[Wrong]Escape dollar sign:

MYSETTINGS+=("--set name=\${TITLE_NAME}")

Or:

MYSETTINGS+=('--set name=${TITLE_NAME}')
mkvpropedit file.mkv ${MYSETTINGS[@]}

For the variable to be expanded when executing the command

./test.sh
+ mkvpropedit file.mkv --edit track:2 --set 'name=${TITLE_NAME}'
The file is being analyzed.
The changes are written to the file.
Done.
2
  • This would fail, or at least not do what you might expect, if $TITLE_NAME contains spaces, tabs or newlines, or globbing patterns. Try with TITLE_NAME='* * *'
    – Kusalananda
    Aug 12, 2021 at 16:47
  • I'm wrong, written as ${TITLE_NAME} instead my title.
    – nezabudka
    Aug 12, 2021 at 16:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.