0

I made a simple script on WSL for windows 11 to mount my pen drive. The default option is to mount it from D:, and I can specify as an argument if the pen drive is in another place.

The script is like this:

#! /bin/bash

[[ $# -eq 0 ]] && sudo mount -t drvfs D: /pen || sudo mount -t drvfs ${1}: /pen

[[ $? == 0 ]] && echo "Good" || echo "Bad"

It works when the pen drive is connected on port D:, but when there is no pen drive connected, the first command executes both sides of the OR.

I am expecting it to execute on the same basis of al if-else statement. If the condition is true, it executes the left side, and if it is false, it executes the right side.

Pau
  • 17
  • 2
  • The precedence of && and || are equal in bash, they are interpreted left-to-right. – rsp May 31 '23 at 09:14
  • In your command `A && B || C`, the `C` will execute if `B` fails. You may want to add parenthesis like `(A && B) || C`? – Jib May 31 '23 at 09:27
  • 3
    The `&& ... ||` trick is *not* equivalent to an `if ... then ... else ... fi`. Don't use it, it just doesn't work. See [BashPitfalls #22](http://mywiki.wooledge.org/BashPitfalls#cmd1_.26.26_cmd2_.7C.7C_cmd3) and ["Can't increment variable in bash shorthand if else condition"](https://stackoverflow.com/questions/61217624/cant-increment-variable-in-bash-shorthand-if-else-condition). – Gordon Davisson May 31 '23 at 09:30
  • 1
    I would just use `if...else` at this point, but if you insist on keeping this form, you can do: `A && { B; true; } || C` – Verpous May 31 '23 at 10:13
  • 5
    Actually, in this case you could skip the conditional entirely, and use an [alternate value substitution](https://stackoverflow.com/questions/9332802/how-to-write-a-bash-script-that-takes-optional-input-arguments/9333006#9333006) to make "D" the default parameter: `sudo mount -t drvfs "${1:-D}:" /pen` – Gordon Davisson May 31 '23 at 10:14
  • The command does not make sense to me. You try to mount the pen drive on D:, and if this fails you try to mount it again for drive $1. However, since you have established that $# is zero, you don't have a parameter $1. – user1934428 May 31 '23 at 10:15
  • @GordonDavisson okay, I thought it was the same. I'm using a normal "if-else" from now on. Thank you. – Pau May 31 '23 at 10:35
  • @user1934428 I thought that this form is the same as an "if-else" clause. What I was trying to do is to mount it differently depending on if the arguments exists or does not. – Pau May 31 '23 at 10:37
  • 1
    @Pau: Of course you can always write a normal `if`....`fi` into a single line, if you want. Statements are separated by **either** newlines **or** semicolons. – user1934428 May 31 '23 at 11:19
  • 1
    Just remember that `A && B || C` does not equal `if A then B else C fi`, it equals `if A then if ! B then C fi; else C fi`. – Ed Morton May 31 '23 at 12:36
  • [Shellcheck](https://www.shellcheck.net/) identifies several problems with the code. The report includes links to more information about the problems and how to fix them. It's a good idea to run [Shellcheck](https://www.shellcheck.net/) on all new and modified shell code. – pjh May 31 '23 at 16:40

1 Answers1

2

This construction (so called one-line if statement) uses executable part as a condition assuming that it's true. But if "true" part returns false, second part will also be executed.

Here's the example:

[[ 0 == 0 ]] && (echo "Good"; exit 1) || echo "Bad"

Condition is true, but executable part after it returns false (exit 1), so [[ 0 == 0 ]] && (echo "Good"; exit 1) part is false and we should check another condition of || operator.

As a result, we are executing both true and false parts.

If you need to fit it into a single line, you can add || true to "true" part:

[[ 0 == 0 ]] && (echo "Good" || true) || echo "Bad"

In this case, even if echo "Good" returns false, the whole "true" part will return true and false second part of || will not be checked.

Taras Khalymon
  • 614
  • 4
  • 11