4

I write a bash script with a --verbose option, where I want to write most lines before I execute them

xinput --set-prop "$TP" "Device Accel Constant Deceleration" $TOUCHPAD_DECELERATION

I would like to have exactly that output with quotes but the variables execuded, so the user can copy and paste that command to execute it again.

This would be the (more and more complicated, if there are a lot of quotes in the command) workaround with writing the line twice:

echo 'xinput --set-prop "'"$TP"'" "Device Accel Constant Deceleration" '"$TOUCHPAD_DECELERATION"
xinput --set-prop "$TP" "Device Accel Constant Deceleration" $TOUCHPAD_DECELERATION

How can I write the outcome ot this line with echo without having to write twice?

rubo77
  • 28,966

2 Answers2

8

You can use the set -x option. set -x will:

Print a trace of simple commands, for commands, case commands, select commands, and arithmetic for commands and their arguments or associated word lists after they are expanded and before they are executed. The value of the PS4 variable is expanded and the resultant value is printed before the command and its expanded arguments.

If you write:

set -x
var="Hello world"
echo "I say: " $var

then the output will be:

+ var='Hello world'
+ echo 'I say:' Hello world
I say: Hello world

The + is the value of PS4. To suppress it, add PS4= after your set -x line, or change it by setting PS4 to another value.

set -x can also be enabled with set -o xtrace. To disable the option again, use set +x.

If there are only some commands you want to print out, you can run them in a subshell:

( set -x ; echo "I say: " $var )
+ echo 'I say:' Hello world
I say: Hello world

Putting the command in parentheses will apply set -x just for this command, and disable it automatically at the end.

rubo77
  • 28,966
Michael Homer
  • 76,565
  • That sounds nice, but How do you combine this with if [ $VERBOSE ]; So it is only printed out, if --verbose is called? – rubo77 Jul 04 '14 at 10:25
  • @rubo77 just put the set -x within the if block: if [ $VERBOSE ]; then set -x; fi or, better, $VERBOSE && set -x. – terdon Jul 04 '14 at 10:31
  • ok, and then put a set +x after each of those lines – rubo77 Jul 04 '14 at 10:40
  • @rubo77 no. You only need to do it once, at the start of your script. – terdon Jul 04 '14 at 10:42
  • If I would put set -x at the top, then the whole script will be printed out while executed, which is far too much. Only some lines should be verbose, so stopping with set +x is great, only a drawback, that it also prints out this last command: + set +x – rubo77 Jul 04 '14 at 10:54
  • The solution for me is: I put this before the first verbose command line: ( $VERBOSE && set -x and in the line after the last command a closing bracket ) – rubo77 Jul 04 '14 at 11:07
2

I use a function for things like this:

#########################################################
# Function to facilitate printing of commands being run #
#########################################################
runthis(){
    echo "$@" 1>&2
    eval "$@"
}

Now, whenever I want to run a command in my script, I run it like this

runthis xinput --set-prop \"$TP\" \"Device Accel Constant Deceleration\" \"$TOUCHPAD_DECELERATION\"
terdon
  • 242,166
  • It doesn't quote parameters, so "Device Accel Constant Deceleration" will become four separate arguments if you copy and paste it. – Michael Homer Jul 04 '14 at 10:08
  • @MichaelHomer no it won't. Note that I am passing that string quoted and so, since "$@" is also quoted, it will be treated as a single argument. You can test this by adding for i in "$@"; do echo "$i"; done in the function. You might also want to read What is the difference between $* and $@?. – terdon Jul 04 '14 at 10:15
  • It outputs (say) xinput --set-prop hello Device Accel Constant Deceleration this is a test. There are no quotes in the output to be copy-pasted (although echo does get them as single arguments because it's $@, yes). – Michael Homer Jul 04 '14 at 10:16
  • @MichaelHomer ah, yes you're quite right. I had missed the part of the question that explained that the objective was to copy/paste the command. Bugger, that complicates things, I was forced to use eval. – terdon Jul 04 '14 at 10:29
  • Ah I see, you need to add backslashes in the call everywhere then, ok. – rubo77 Jul 04 '14 at 10:37
  • @rubo77 yes. Michael's answer avoids that if you're OK with the format. – terdon Jul 04 '14 at 10:42
  • @MichaelHomer - the set -x behavior of parameter quoting is hardly a guaranteed thing either. it is bash's behavior, yes, but should be relied upon. – mikeserv Aug 05 '14 at 07:34
  • @mikeserv: It's explicitly a Bash question, twice over. You can rely on Bash having its own behaviour. – Michael Homer Aug 05 '14 at 07:37