dougkramer
dougkramer

Reputation: 1115

How to echo a command before executing it

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

Answers (2)

anubhava
anubhava

Reputation: 785156

Proper way to store a command line and execute it later can be done in 2 ways:

  1. Use BASH arrays
  2. Use BASH function

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

Gordon Davisson
Gordon Davisson

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

Related Questions