Reputation: 1115
I want to echo a command before executing it. This command replaces string 'PROJECT_NAME' with 'Hello world' in file.yaml.
PROJECT_NAME="Hello world"
CMD="replace.py 'PROJECT_NAME' '${PROJECT_NAME}' file.yaml"
echo "${CMD}"
${CMD}
What's the proper way to quote this string so the command will execute and the echo will show the command?
Notice that the value of variable ${PROJECT_NAME} contains spaces, and replace.py takes exactly 3 args.
I tried escaping all the quotes (\') and dollar signs (\$). I tried swapping single quotes (') with double quotes (").
With some quoting, it would treat each word in "Hello world" as a separate arg, so replace.py would choke on "world" not being the filename it expects.
I have read http://mywiki.wooledge.org/BashFAQ/050 but still can't figure it out. I prefer not to duplicate the command or use set -x (because it prints set +x when using that to turn it off).
Upvotes: 2
Views: 2928
Reputation: 785156
Proper way to store a command line and execute it later can be done in 2 ways:
1. Using BASH array:
PROJECT_NAME="Hello world"
cmd=("replace.py" "PROJECT_NAME" "$PROJECT_NAME" "file.yaml")
printf "%q " "${cmd[@]}"; echo
"${cmd[@]}"
Now run it as:
bash ./script.sh
replace.py PROJECT_NAME Hello\ world file.yaml
Note escaping of space by %q
of printf
. You can run this command line from shell directly also to verify it again i.e.
replace.py PROJECT_NAME Hello\ world file.yaml
2. Using a function:
#!/bin/bash
PROJECT_NAME="Hello world"
cmd() {
set -x
replace.py "PROJECT_NAME" "$PROJECT_NAME" "file.yaml"
set +x
}
cmd
Now run it as:
./script.sh
and you will get the similar output:
+ replace.py PROJECT_NAME 'Hello world' file.yaml
...
...
set +x
You can run this command line also:
replace.py PROJECT_NAME 'Hello world' file.yaml
Upvotes: 2
Reputation: 125798
One possibility is to use an array instead of a plain string, and then use printf
's %q
format to re-add quotes and/or escapes where needed:
PROJECT_NAME="Hello world"
CMD=(replace.py 'PROJECT_NAME' "${PROJECT_NAME}" file.yaml)
printf "%q " "${CMD[@]}"; printf "\n"
"${CMD[@]}"
Note that this may not use the same quoting/escaping method you used when you created the array; for example, I tried the above and it printed:
replace.py PROJECT_NAME Hello\ world file.yaml
Which is equivalent to:
replace.py 'PROJECT_NAME' "Hello world" file.yaml
...just a different way of writing the same command.
Upvotes: 1