Discussion:
built-in '[' and '/usr/bin/[' yield different results
Service
2018-11-13 16:29:42 UTC
Permalink
Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64'
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-pc-linux-gnu'
-DCONF_VENDOR='pc' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash'
-DSHELL -DHAVE_CONFIG_H   -I.  -I../. -I.././include -I.././lib
-Wdate-time -D_FORTIFY_SOURCE=2 -g -O2
-fdebug-prefix-map=/build/bash-vEMnMR/bash-4.4.18=.
-fstack-protector-strong -Wformat -Werror=format-security -Wall
-Wno-parentheses -Wno-format-security
uname output: Linux COOL4 4.4.0-17134-Microsoft #345-Microsoft Wed Sep
19 17:47:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 4.4
Patch Level: 19
Release Status: release

Description:
    It seems that the built-in 'test' does not work properly when run
in a sub-shell.

Repeat-By:
    Under Windows 10, WSL.
    Start "bash", terminal with shell pops up.

    1. This works when typed directly:

    # make everything explicit
    export PATH=""
    # ensure that file1 exists and that file2 does not exist
    /bin/touch file1
    /bin/rm -f file2
    # built-in
    if          [ file1 -nt file2 ]; then echo nt; else echo not_nt; fi
    # external
    if /usr/bin/[ file1 -nt file2 ]; then echo nt; else echo not_nt; fi

    # Output is as expected:
    nt
    nt

    2. This does not work:

    # Put the above commands into a script, say check.sh
    # Run with: /bin/sh < check.sh
    # Or      : /bin/sh ./check.sh
    # Or      : /usr/bin/env ./check.sh

    # Output is always not ok:
    not_nt
    nt
Greg Wooledge
2018-11-13 17:54:31 UTC
Permalink
Post by Service
    Under Windows 10, WSL.
Then why did you send this to a debian.org address?
Post by Service
    Start "bash", terminal with shell pops up.
    # Put the above commands into a script, say check.sh
    # Run with: /bin/sh < check.sh
    # Or      : /bin/sh ./check.sh
In both of these cases, you're explicitly calling /bin/sh rather than
bash. So you are not using bash's builtin [. You are using WSL's sh's
builtin [ command. You might start by identifying which shell WSL uses
as its sh.
Post by Service
    # Or      : /usr/bin/env ./check.sh
In this case, it would use the shebang line of check.sh to decide which
shell to run. However, I suspect you did not include a shebang line at
all. In that case, /usr/bin/env will decide how to handle the exec
format error. It might decide to spawn sh for you, in which case this
would explain why the result matches the first two results.
Eric Blake
2018-11-13 17:54:29 UTC
Permalink
Post by Service
    # ensure that file1 exists and that file2 does not exist
There's your problem. It is inherently ambiguous what timestamp to use
when a file is missing (infinitely new or infinitely old, or always an
error for not existing); bash's -nt picked one way, while other shells
have picked the other. POSIX is silent on the matter (-nt is an
extension outside of POSIX), so there is nothing portable you can rely on.
Post by Service
    /bin/touch file1
    /bin/rm -f file2
    # built-in
    if          [ file1 -nt file2 ]; then echo nt; else echo not_nt; fi
    # external
    if /usr/bin/[ file1 -nt file2 ]; then echo nt; else echo not_nt; fi
    nt
    nt
That is, bash's builtin '[' and coreutil's external '[' happened to pick
the same thing: a missing file is treated as infinitely old.
Post by Service
    # Put the above commands into a script, say check.sh
    # Run with: /bin/sh < check.sh
    # Or      : /bin/sh ./check.sh
    # Or      : /usr/bin/env ./check.sh
    not_nt
    nt
Most likely, this is because your /bin/sh is not bash, but probably
dash, and dash has picked a missing file as being treated as always an
error. That does not make it a bug in bash, though, but a difference in
behavior of your /bin/sh.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org
Ilkka Virta
2018-11-13 17:54:28 UTC
Permalink
Post by Service
    # Put the above commands into a script, say check.sh
    # Run with: /bin/sh < check.sh
    # Or      : /bin/sh ./check.sh
    # Or      : /usr/bin/env ./check.sh
    not_nt
    nt
$ cat check.sh
export PATH=""
/bin/touch file1
/bin/rm -f file2
if [ file1 -nt file2 ]; then echo nt; else echo not_nt; fi
if /usr/bin/[ file1 -nt file2 ]; then echo nt; else echo not_nt; fi

$ bash ./check.sh
nt
nt

$ /bin/sh ./check.sh
not_nt
nt

Isn't that Windows Linux thingy based on Ubuntu? /bin/sh isn't Bash by
default on Debian and Ubuntu, so it might be you're just not running the
script with Bash.
--
Ilkka Virta / ***@iki.fi
Loading...