bash: проверка присваивания И условия во вложенном операторе? - PullRequest
0 голосов
/ 26 мая 2020

Я ищу компактную строку кода, которая дважды использует значение выражения в одном вложенном операторе

  • для присвоения переменной и

  • проверка условия нового значения переменной по сравнению с другим значением

Вопросы:

  1. Возможна ли эта компактная форма в bash?

  2. Если да, нужно ли мне опасаться негативных побочных эффектов (неожиданных результатов)?

Вы, конечно, можете выполнить присвоение раньше:

$ local bus_connection="$(lsblk -dn -o TRAN /dev/sdd)"
$ test "$bus_connection" = "usb" || { echo "not usb"; exit 1 }
$ echo "$bus_connection"
usb

-> Работает!

Если переменная bus_connection нужна только в случае успеха, вы также можете сделать:

$ test "$(lsblk -dn -o TRAN /dev/$DEVICE)" = "usb" \
    && local bus_connection="usb" \
    || { echo "not usb"; exit 1 }
$ echo "$bus_connection"
usb

-> Тоже работает! (Не используйте 'local' в командной строке!)

В C (++) и некоторых других скомпилированных языках высокого уровня вы можете сделать это более компактным (предположим, что функция c '' lsblk '):

if( ( bus_connection = lsblk( ... ) ) == "usb" ) ; else {printf("not usb"); exit 1;}

В bash Я пробовал:

$ test $(bus_connection="$(lsblk -dn -o TRAN /dev/sdd)") = "usb" || echo "not usb" 
bash: test: =: unary operator exspected.
not usb

-> Сообщение об ошибке и неправильный результат!

Я тестировал с целочисленным регистром и нашел:

$ sum=0
$ result=12
$ test $((sum=$sum+$result)) -gt 20 && echo "true"
$ test $((sum=$sum+$result)) -gt 20 && echo "true"
true

-> Работает! Отрицательные побочные эффекты?

Аналог:

$ test $((bus_connection="$(lsblk -dn -o TRAN /dev/sdd)")) = "usb" || echo "not usb"
not usb

-> Нет сообщения об ошибке, но неверный результат!

$ echo "$bus_connection"
0

-> Ах, очевидно, bash лечит переменная bus_connection теперь имеет целочисленный тип.

Есть предложения для строк?

1 Ответ

2 голосов
/ 26 мая 2020

Возможна ли эта компактная форма в bash

Нет, это не так, по крайней мере, без специального кода для ее обработки. Подстановка команд выполняется в подоболочке - изменения не будут видны в родительской оболочке. Расширение Arithmeti c выполняет только операции arithmeti c - все строки преобразуются в числа (как если бы atoi()/strtol()), если никакие цифры не сканируются, они преобразуются в 0.

Обычный способ - просто чтобы присвоить переменной значение && с последующим тестом - это позволяет проверить статус выхода команды и получить результат.

if ! {
     bus_connection="$(lsblk -dn -o TRAN /dev/sdd)" &&
     test "$bus_connection" = "usb"
}; then
   echo "not usb"
   exit 1
fi

или просто игнорировать статус выхода lsblk:

if
   bus_connection="$(lsblk -dn -o TRAN /dev/sdd)"
   ! test "$bus_connection" = "usb"
then
   echo "not usb"
   exit 1
fi

Тем не менее, вы можете написать свою собственную функцию, которая использует bash namereference для присвоения первого аргумента второму и возвращает test s статус выхода:

test_and_assign() {
   declare -n _test_and_assign_ref=$1
   shift
   _test_and_assign_ref="$1"
   test "$@"
}

$ test_and_assign bus_connection "$(echo 123)" = "usb"
$ echo "$? $bus_connection"
1 123
$ test_and_assign bus_connection "$(echo usb)" = "usb"
$ echo "$? $bus_connection"
0 usb

Обратите внимание, что здесь , как и в вашем первом фрагменте кода с local bus_connection=$(...), статус выхода подстановки команды игнорируется.

...