Why is the 'if [ $1="1" ]' branch always selected even if $1 is not 1?

35,174

Solution 1

Using spaces fixes your problem.

if [ "$1" = 1 ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ "$1" = 2 ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ "$1" = 3 ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

Though this is neater:

#!/bin/bash

action=$1
shift
files=("$@")
case $action in  
  1) mv -- "${files[@]}" ~/lab/Sun     ;;
  2) mv -- "${files[@]}" ~/lab/Moon    ;;
  3) mv -- "${files[@]}" ~/lab/Earth   ;;
esac

Solution 2

First obvious thing is you should provide spaces between the arguments of [, test or [[:

if [ "$1" = 1 ];

When in Bash, using [[ ]] is recommended as it doesn't do things unnecessary for conditional expression like word splitting and pathname expansion. Quoting around double-quotes is also not needed. A more readable operator == can also be used.

if [[ $1 == 1 ]];

Added note: If second operand also contains variables, quoting is necessary as it may be subject to pattern matching if it contains recognizable characters like *, ?, [], etc.. If extended globbing or pattern matching is enabled with shopt -s extglob, other forms like @(), !(), etc. will also be recognized as patterns. See Pattern Matching.

With operators like < and > it may still be necessary as I had once encountered a bug where not quoting the second argument caused different results.

As for the first operand, nothing applies.

Consider this simpler variation as well:

case "$1" in
1)
    mv -- "${@:2}" ~/lab/Sun
    ;;
2)
    mv -- "${@:2}" ~/lab/Moon
    ;;
3)
    mv -- "${@:2}" ~/lab/Earth
    ;;
esac

Or condensed:

case "$1" in
1) mv -- "${@:2}" ~/lab/Sun ;;
2) mv -- "${@:2}" ~/lab/Moon ;;
3) mv -- "${@:2}" ~/lab/Earth ;;
esac

"${@:2}" is a form of substring expansion or array member expansion where 2 is the offset. This makes expansion start at the second value. With this we may not need to use shift.

The added -- prevents mv from recognizing filenames starting with dash (-) as invalid options.

Solution 3

To answer the question of why this is happening, this behavior of [ aka test is documented in POSIX:

In the following list, $1, $2, $3, and $4 represent the arguments presented to test:

[...]

1 argument:

Exit true (0) if $1 is not null; otherwise, exit false.

You're passing it 1 argument, 2=1, which is not null, and therefore test exits with success.

As other posts (and shellcheck) point out, if you wanted to compare for equality, you would instead have to pass the 3 arguments 2, = and 1.

Solution 4

I just want to recommend a portable yet also neater alternative. Bash is not universal (and if you don't need universal, why are you writing a shell script?)

#! /bin/sh
action="$1"
shift
case "$action" in
    1) dest=Sun   ;;
    2) dest=Moon  ;;
    3) dest=Earth ;;
    *) echo "Unrecognized action code '$action' (must be 1, 2, or 3)" >&2; exit 1 ;;
esac
mv -- "$@" ~/lab/"$dest"

(Note to pedants: yes, I know the quotes around $action on the case "$action" in line are unnecessary, but I feel it is best to put them there anyway, so that future readers don't have to remember that.)

Share:
35,174

Related videos on Youtube

Zen
Author by

Zen

Updated on September 18, 2022

Comments

  • Zen
    Zen almost 2 years

    I have a shell script named 'teleport.sh' like this:

    if [ $1="1" ];
        then
        shift
            mv "$@" ~/lab/Sun
    elif [ $1="2" ];
        then
        shift
            mv "$@" ~/lab/Moon
    elif [ $1="3" ];
        then
        shift
            mv "$@" ~/lab/Earth
    fi
    

    When I execute:

    sh teleport.sh 2 testfile
    

    This testfile is moved to the ~/lab/Sun directory, which confuses me a lot as I didn't pass 1 or '1' to that script.

    What's wrong here?

  • Арсений Черенков
    Арсений Черенков almost 10 years
    shouldn't you break in each case ?
  • Mat
    Mat almost 10 years
    @Archemar: no, there's no fall-through (contrary to a lot of other languages).
  • Stéphane Chazelas
    Stéphane Chazelas almost 10 years
    @Mat, there's fall-through if you use ;& instead of ;; (ksh, bash, zsh only). But then break still does not prevent the fall-through, break is only to break out of loops.
  • Stéphane Chazelas
    Stéphane Chazelas almost 10 years
    That's not arguments to if but to the [ command.
  • konsolebox
    konsolebox almost 10 years
    @StéphaneChazelas Yeah it's a quick-answer mistake.
  • echristopherson
    echristopherson almost 10 years
    Yes, the spaces are necessary, but I'm wondering why the original $1="1" syntax "works" at all (producing a true result). How does the [/test builtin actually interpret that expression?
  • bishop
    bishop almost 10 years
    @echristopherson Without spaces, test sees only one argument (an "l-value"). Without the other arguments (an "operator" and an "r-value"), there is nothing to test, so test just says "ok, well yeah, you gave me something and that must be vacuously true".
  • jdh8
    jdh8 almost 10 years
    $1="1" is $1 concatenated by =1
  • nyuszika7h
    nyuszika7h almost 10 years
    Correction: double quotes are only not needed on the left hand side! [[ $foo == $bar ]] will perform pattern matching, but [[ $foo == "$bar" ]] won't.
  • konsolebox
    konsolebox almost 10 years
    @nyuszika7h Of course. Thanks for telling. I had only thought about the current subject. It's now updated.
  • Ruslan
    Ruslan almost 10 years
    "Bash is not universal (and if you don't need universal, why are you writing a shell script?)" — this seems to imply that bash scripting has no use cases.
  • zwol
    zwol almost 10 years
    @Ruslan Yes, that is my considered opinion. Write portable /bin/sh scripts, if you need that; otherwise, write in a scripting language that is less terrible than shell. The basic Perl interpreter is in fact more likely to be present in legacy proprietary environments and cut-down embedded environments than Bash is.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten almost 10 years
    In a sense this is the only the answer that has answered the question asked (rather than giving one or more recipes that do what the OP wanted to accomplish), and shell can be sufficiently mysterious that know why it does the things it does is useful.
  • Zen
    Zen almost 10 years
    when using case, how to set an action to execute when none of the conditions is meet?