Чому гілка 'if [$ 1 = "1"]' завжди вибирається, навіть якщо $ 1 не є 1?


10

У мене є сценарій оболонки з назвою "teleport.sh", як це:

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

Коли я виконую:

sh teleport.sh 2 testfile

Це testfileпереміщено до ~/lab/Sunкаталогу, що мене сильно плутає, оскільки я не передав 1 або "1" цьому сценарію.

Що тут не так?


1
+1 для лабораторії , Сонця , Місяця , Землі та телепорту . Але завжди слід подвоїти розширення цитат ( $var, $(cmd)і навіть `cmd`[яким $(cmd)слід віддати перевагу]). Є деякі крайні випадки, коли не потрібно цитувати, але завжди робити це не зашкодить.
nyuszika7h

@ nyuszika7h, чи не слід подвійну цитату означати "$ var" і "$ cmd"? яка користь круглої дужки, яку ви згадали вище?
Дзен

$(cmd)- це підміна команд (в основному) те саме, що `cmd`. Дивіться mywiki.wooledge.org/CommandSubstitution and mywiki.wooledge.org/BashFAQ/082
nyuszika7h

Відповіді:


19

Використання пробілів виправляє вашу проблему.

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

Хоча це акуратніше:

#!/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

3
Так, пробіли необхідні, але мені цікаво, чому оригінальний $1="1"синтаксис взагалі "працює" (даючи справжній результат). Як [/ testвбудований насправді інтерпретує цей вираз?
echristopherson

5
@echristopherson Без пробілів testбачить лише один аргумент ("l-значення"). Без інших аргументів ("оператор" і "r-значення") нічого не можна перевірити, тому testпросто сказано "добре, ну так, ти мені щось дав, і це повинно бути правдиво правдою".
єпископ

2
$1="1"це $1зчіплюються по=1
jdh8

при використанні випадку, як встановити дію, яку слід виконати, коли жодна з умов не виконується?
Дзен

11

По- перше очевидно , що це ви повинні забезпечити прогалини між аргументами [, testабо [[:

if [ "$1" = 1 ];

Якщо в Bash, використання [[ ]]рекомендується, оскільки це не робить непотрібних для умовного вираження, таких як розділення слів і розширення імені шляху. Цитування подвійних лапок також не потрібно. ==Можна також скористатися більш читаним оператором .

if [[ $1 == 1 ]];

Додано примітка: Якщо другий операнд також містить змінні, цитує необхідно , так як це може бути при умови зіставлення з зразком , якщо він містить впізнавані символи , такі як *, ?, []і т.д .. Якщо розширена підстановки або відповідний шаблон включений shopt -s extglob, інші форми , як @(), !()і т.д. також будуть визнані зразками. Дивіться відповідність шаблонів .

З такими операторами, як, <і >це все ще може знадобитися, оскільки я колись стикався з помилкою, коли не цитування другого аргументу спричинило різні результати.

Щодо першого операнда, то нічого не стосується.

Розглянемо також цю простішу варіацію:

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

Або конденсований:

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

"${@:2}"є формою розширення підрядків або розширенням члена масиву, де 2відбувається зміщення. Це змушує розпочати розширення з другого значення. Цим ми можемо не потребувати використання shift.

Додане --заважає mvрозпізнавати назви файлів, починаючи з тире ( -), як недійсні параметри.


ви не повинні ламати в кожному випадку?
Архемар

2
@Archemar: ні, немає проникнення (всупереч багатьом іншим мовам).
Мат

2
@Mat, пропускний шлях, якщо ви використовуєте ;&замість ;;(ksh, bash, zsh). Але тоді breakвсе ще не заважає просідати, варто breakлише вирватися з петель.
Стефан Шазелас

Це не аргументи, ifа [команда.
Стефан Шазелас

1
Виправлення: подвійні лапки не потрібні лише на лівій стороні! [[ $foo == $bar ]]виконає відповідність шаблону, але [[ $foo == "$bar" ]]не буде.
nyuszika7h

7

Для того, щоб відповісти на питання про те, чому це відбувається, така поведінка [інакше testяк описано в стандарті POSIX :

У наступному списку $ 1, $ 2, $ 3 і $ 4 представляють аргументи, представлені для тестування:

[...]

1 аргумент:

Вийдіть true (0), якщо $ 1 не є нульовим; в іншому випадку вийдіть помилково.

Ви передаєте йому 1 аргумент, 2=1який не є нульовим, тому testвиходить із успіхом.

Як і інші посади (і shellcheck ) вказують, якщо ви хочете , щоб порівняти рівності, ви б замість цього повинні пройти 3 -х аргументів 2, =і 1.


3
У певному сенсі це єдина відповідь, яка відповіла на поставлене запитання (а не дає одного чи декількох рецептів, які роблять те, що хотіла виконати ОП), і оболонка може бути досить загадковою, щоб знати, чому це робить те, що робить, це корисно .
dmckee --- кошеня колишнього модератора

1

Я просто хочу порекомендувати портативну, але ще й охайну альтернативу. Bash не є універсальним (і якщо вам не потрібен універсальний, чому ви пишете сценарій оболонки?)

#! /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"

(Зверніть увагу на педантів: так, я знаю, що цитати $actionв case "$action" inрядку непотрібні, але я вважаю, що найкраще все-таки розміщувати їх там, щоб майбутнім читачам не потрібно було пам’ятати про це.)


1
"Bash не є універсальним (і якщо вам не потрібен універсальний, чому ви пишете сценарій оболонки?)" - це, мабуть, означає, що сценарій bash не має випадків використання.
Руслан

@ Руслан Так, це моя думка. Пишіть портативні /bin/shсценарії, якщо вам це потрібно; інакше пишіть мовою сценаріїв, яка менш жахлива, ніж оболонка. Насправді, основний інтерпретатор Perl є більш імовірним присутнім у застарілих фірмових середовищах та вбудованих середовищах, ніж Bash.
zwol
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.