28
$ time if [[ $dir2 -ot $dir1 ]] ; then open $dir2 ; else open $dir1 ; fi
real    0m0.055s
user    0m0.023s
sys     0m0.018s

$ time  [[ $dir2 -ot $dir1 ]] && open $dir2 || open $dir1
real    0m0.000s
user    0m0.000s
sys     0m0.000s

At above, I compared two methods for testing which directory is older and then opening the older directory. I guess I'm just confused why the && and || operators right after a test has the same functionality of an if/then test, and why it's faster. Are there advantages/disadvantages to each?

αғsнιη
  • 40,939
  • 15
  • 71
  • 114
cp10111
  • 297
  • 3
  • 4
  • 8
    They are *not* functionally equivalent. In the second, if open dir2 fails, open dir1 will run. That cannot be the case in the first. – D. Ben Knoble Jun 27 '20 at 01:44
  • 2
    Related: [How can I time a pipe?](https://unix.stackexchange.com/q/364156)? – terdon Jun 28 '20 at 14:08

1 Answers1

70

time always times the directly following pipeline.

A pipeline is a sequence of one or more commands (simple or compound) separated by one of the control operators | or |&.

if ... fi is a single compound command, and [[ ... ]] is one command. The part after && is not measured by time because foo && bar is a list of commands.

Compare:

$ time if true; then sleep 1; fi

real    0m1.004s
user    0m0.000s
sys     0m0.000s


$ time true && sleep 1

real    0m0.000s
user    0m0.000s
sys     0m0.000s

# example of pipeline separated with |:
$ time true | sleep 1

real    0m1.004s
user    0m0.000s
sys     0m0.000s

To measure time from all of the second commands, you can group them by putting it in (curly) brackets:

$ time { true && sleep 1; }

real    0m1.003s
user    0m0.000s
sys     0m0.000s
ilkkachu
  • 133,243
  • 15
  • 236
  • 397
pLumo
  • 22,231
  • 2
  • 41
  • 66
  • 5
    Good catch! The `time` keyword only applies to the command (compound or simple) that it precedes. So to time properly in the second command, they would need to use `time open ... || time open ...`. – Kusalananda Jun 25 '20 at 16:33
  • 11
    To be hideously exact, [Bash's `time` times a _pipeline_](https://www.gnu.org/software/bash/manual/bash.html#Pipelines), which can then contain simple or compound commands. `if-then-fi` is a compound command, which can be part of a pipeline, but `foo && bar` is a list of commands that can't be without being grouped in `{ }` or `( )`. – ilkkachu Jun 25 '20 at 16:44
  • 1
    Try `command time ...` ;) – 0xC0000022L Jun 26 '20 at 10:00
  • The `[ : ]` construct is completely different from just `:`. `:` is a command that simply exits with 0 (true) status (also called `true`). The `[` is another command that runs various tests, by default testing whether the argument is non-empty (which `:` is). If called with the name `[`, rather than the alternate name `test`, it requires an additional `]` argument. – Jan Hudec Jun 26 '20 at 10:59
  • @JanHudec, yes, though in practice they both return a truthy exit status, so it doesn't matter here. The time taken by both is also insignificant with the `sleep 1` right there next to them. – ilkkachu Jun 26 '20 at 12:24
  • I used `[ : ]` instead of simply `true` because OP used the similar `[[ ... ]]` in the question. – pLumo Jun 26 '20 at 12:28
  • 1
    @pLumo, 1. OP used `[[` consistently in both cases and 2. OP used it in the intended way while `[ : ]` basically works by accident. – Jan Hudec Jun 26 '20 at 12:39
  • 1
    @JanHudec, what do you mean "by accident"? `test` is defined to exit with a zero status (truthy) if only gets one argument that's not the empty string. (or one plus the tailing `]` in the case of `[`). Anyway, for the difference between `if` and `&&` it doesn't really matter how that truthy condition is acquired, so I edited this to use just `true` in every case. Perhaps that would be less distracting? – ilkkachu Jun 26 '20 at 12:57
  • 2
    @ilkkachu, yes, the edit is good. What I mean with “by accident” is that the way it actually works is unlikely what author had in mind when they wrote it. – Jan Hudec Jun 26 '20 at 13:04