The officially official Devuan Forum!

You are not logged in.

#1 2018-01-02 12:43:58

msi
Member
Registered: 2017-02-04
Posts: 143  

[Solved] Bash: Behavior of `test -x`

Trying to implement a check for the availability of a given text editor inside a bash script, I discovered that test -x needs its argument to be put in quotes if the argument is a command substitution. Maybe someone can exlplain to me why.

Here's an example of what happens if you don't put the quotes:

$ type -p nano  # nano is installed
/usr/bin/nano
$ type -p gedit  # gedit is not installed
$
$if [ -x $(type -p nano) ]; then echo "found executable"; else echo "command not found/not an executable"; fi
found executable
$ if [ -x $(type -p gedit) ]; then echo "found executable"; else echo "command not found/not an executable"; fi
found executable  # This is strange.
$
$ if [ -x "$(type -p nano)" ]; then echo "found executable"; else echo "command not found/not an executable"; fi
found executable
$ if [ -x "$(type -p gedit)" ]; then echo "found executable"; else echo "command not found/not an executable"; fi
command not found/not an executable

Last edited by msi (2018-01-05 02:21:20)

Offline

#2 2018-01-02 14:48:03

fsmithred
Administrator
Registered: 2016-11-25
Posts: 2,409  

Re: [Solved] Bash: Behavior of `test -x`

Well, without the quotes it tells me that gedit is found/executable, but gedit is not installed here. I'l take a guess and say that -x is testing the executability of the command substitution. When you quote it, you're telling the shell to expand what's inside the quotes, so -x tests the result of that.

For more complication, try it with double-brackets for test:

if [[ -x $(type -p gedit) ]]; then echo "found executable"; else echo "command not found/not an executable"; fi
command not found/not an executable

Offline

#3 2018-01-02 19:16:31

msi
Member
Registered: 2017-02-04
Posts: 143  

Re: [Solved] Bash: Behavior of `test -x`

fsmithred wrote:

I'l take a guess and say that -x is testing the executability of the command substitution. When you quote it, you're telling the shell to expand what's inside the quotes, so -x tests the result of that.

That sounds like a reasonable explanation, though I'm not sure it's accurate. But a look at exit codes also points into that direction:

$ test -x /usr/bin/whoami
$ echo $?
0
$ test -x /usr/bin/whoarethey
$ echo $?
1
$ test -x $(type -p whoami)
$ echo $?
0
$ test -x $(type -p whoarethey)
$ echo $?
0
$ test -x "$(type -p whoami)"
$ echo $?
0
$ test -x "$(type -p whoarethey)"
$ echo $?
1

Offline

#4 2018-01-03 00:06:22

ralph.ronnquist
Administrator
From: Clifton Hill, Victoria, AUS
Registered: 2016-11-30
Posts: 1,106  

Re: [Solved] Bash: Behavior of `test -x`

The explanation is in how the bash expands commands, and how "-x" works.

Without quotes, a phrase like

[ -x $(type -p gedit)]

gets "expanded" into the following phrase when "gedit' is missing (aka "type" returns nothing):

[-x  ]

i.e., "-x" without argument, and that succeeds. Whereas with quotes around it, the expansion is

[ -x "" ]

i.e. "-x" with an empty string as argument, and that fails.

Perhaps your statement should have been like

if type -p $program > /dev/null ; then echo UGH ; else echo NÖF; fi

That would be using the return code of "type" as condition, with 0 meaning success and non-0 meaning fail.

Offline

#5 2018-01-04 21:19:06

msi
Member
Registered: 2017-02-04
Posts: 143  

Re: [Solved] Bash: Behavior of `test -x`

ralph.ronnquist wrote:

The explanation is in how the bash expands commands, and how "-x" works.

Without quotes, a phrase like

[ -x $(type -p gedit)]

gets "expanded" into the following phrase when "gedit' is missing (aka "type" returns nothing):

[-x  ]

i.e., "-x" without argument, and that succeeds. Whereas with quotes around it, the expansion is

[ -x "" ]

i.e. "-x" with an empty string as argument, and that fails.

I see. Thanks for explaining that.

ralph.ronnquist wrote:

Perhaps your statement should have been like

if type -p $program > /dev/null ; then echo UGH ; else echo NÖF; fi

That would be using the return code of "type" as condition, with 0 meaning success and non-0 meaning fail.

The problem with this is that, according to man bash, type -p only...

returns the name of the disk file that would be executed if name were specified as a command name, or nothing if ``type -t name'' would not return file.

In other words, it just checks if $program is in your $PATH, not if it is executable.

So, the right way to perform that check would be to use text -x, but with proper quoting:

if [ -x "$(type -p "$program")" ]
then
  [commands]
else
  [commands]
fi

Last edited by msi (2018-01-04 21:24:31)

Offline

Board footer