Почему 'if ' ветвь всегда выбрана, даже если $ 1 не 1?

У меня есть сценарий оболочки с именем «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 на этот скрипт.

Что здесь не так?

4 Solutions collect form web for “Почему 'if ' ветвь всегда выбрана, даже если $ 1 не 1?”

Использование пробелов устраняет вашу проблему.

 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 

Прежде всего, вы должны предоставить пробелы между аргументами [ , test или [[ :

 if [ "$1" = 1 ]; 

Когда в Bash рекомендуется использовать [[ ]] поскольку он не делает ненужных для условного выражения, таких как разбиение слов и расширение пути. Цитирование двоичных кавычек также не требуется. Также можно использовать более читаемый оператор == .

 if [[ $1 == 1 ]]; 

Добавлено примечание: если второй операнд также содержит переменные, необходимо использовать цитирование, поскольку оно может быть подвергнуто сопоставлению с образцом, если оно содержит распознаваемые символы, такие как * ? , [] и т. д. Если расширенное globbing или сопоставление с образцом включено с 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 распознавать имена файлов, начиная с тире ( - ) как недопустимые параметры.

Чтобы ответить на вопрос о том, почему это происходит, это поведение [ aka test зарегистрировано в POSIX :

В следующем списке $ 1, $ 2, $ 3 и $ 4 представляют аргументы, представленные для теста:

[…]

1 аргумент:

Exit true (0), если $ 1 не является нулевым; в противном случае выйдите из ложного.

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

Как указывают другие сообщения (и shellcheck ), если вы хотите сравнить для равенства, вам вместо этого нужно будет передать 3 аргумента 2 , = и 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 строке не нужны, но я считаю, что лучше всего их туда поместить, чтобы будущие читатели не должны были это помнить. )

  • Сравнение строк со специальными символами с использованием if-loop не работает
  • Какой смысл использовать «x $ START_DAEMON» = «xyes» здесь? (заголовок х)
  • Использование команды test для создания каталога, если он не существует
  • Тест Bash: что делает «= ~»?
  • что означает флаг -z здесь
  • Какова цель выполнения квадратных скобок
  • ksh, если с несколькими условиями
  • Я хочу сравнить два списка и распечатать вывод разности из списка 1
  • Что означает -o означает «если»?
  • никогда не используйте оператор `-a` или` -o` с `[`
  • Разница между `]` и `(($ a <2))`
  • bash howto script - если нет ответа или ответа mutliline
  • Linux и Unix - лучшая ОС в мире.