Эффективно сравнивать, если переменная существует и не равна

Эффективно сравнивать, если переменная существует и не равна

У меня проблема с shсинтаксисом скрипта. Я написал скрипт для своего роутера Asus. Скрипт работает отлично. Но у меня есть эта строка:

if [[ "$OldIP" && "$StartIP" != "$OldIP" ]]; then echo OK; fi

Он должен быть истинным ( и выполняться echo OK) только если $StartIPи $OldIPне являются одинаковыми. Строка работает, но я хотел бы видеть ее более эффективной. Переменные содержат допустимые IP-адреса.

В некоторых случаях $OldIPничего не будет назначено (не инициализировано). Но если $OldIPне существует, значит, в моей оболочке они не одинаковы!

Я не хочу, чтобы строка делала: если $OldIPне существует -> проверить, различны ли они -> запустить echo OK.

Я хочу, чтобы строка означала: а) если $OldIPне существует -> конец. плюс б) если $OldIPсуществует -> проверить, различны ли они -> запустить echo OK.

Так что я хотел бы как-то удалить "$OLDIP" &&, если это возможно. Не настоящая проблема; интересно узнать :)

Что-то вроде того (но это не работает):

if [ [ "$OldIP" ] != "$StartIP" ]; then echo OK; fi

или

if [ $OldIP != "$StartIP" ]; then echo OK; fi

который делает то, что я хочу, но жалуется, когдаСтарый IPпусто (но работает нормально)

пока

if [ "$OldIP" != "$StartIP" ]; then echo OK; fi

работает, но игнорирует этоСтарый IPпусто

решение1

Строго говоря, переменная не существует, если ей никогда не было присвоено значение или если ей было присвоено unset. Переменная с пустой строкой в ​​качестве значениясуществует.

Стандартное расширение параметра ${variable+value}заменит строку, valueесли variableсуществует (не является неустановленным или пустым). Это можно использовать для проверки существования следующим образом:

if [ "${OldIP+x}" = "x" ] && [ "$OldIP" != "$StartIP" ]; then
    # code
fi

Чтобы проверить, OldIPсуществует ли в bash, используйте -vтест:

 if [[ -v OldIP ]] && [[ "$OldIP" != "$StartIP" ]]; then
     # code
 fi

Это выполнит сравнение строк между $OldIPи $StartIPтолько если OldIPбыло установлено ранее (даже если оно было установлено на пустую строку). Обратите внимание, что тест -vпринимаетимяпеременной.

решение2

Я думаю, что ваш скрипт и так довольно эффективен. Вы определенно не тратите много циклов на это.

Другой способ записать логику:

{ test -n "$OldIP" || test "$StartIP" != "$OldIP"; } && echo OK

Здесь говорится: «Если установлен OldIP или OldIP и StartIP различны, то вывести OK».

Помните, что это [еще одно название, о test(1)котором вы можете прочитать здесь man 1 test.

Редактировать: Как указал Жиль, старайтесь избегать создания подоболочек ( ... )там, где они не нужны.

{ ... ; }может использоваться для группировки команд без запуска новой оболочки.

Краткая справка:http://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html

решение3

Это делает то, о чем вы просите:

[ "${OldIP:-"$StartIP"}" != "$StartIP" ] && echo "OK"

А также это (более сложное):

${OldIP:+false} || { [[ $OldIP != $StartIP ]] && echo "OK"; }

решение4

Если вы используете bash, вы можете использовать замену по умолчанию:

[[ "${OldIP:=oldunset}" != "${StartIP?StartUnset" ]] && echo "OK"

Синтаксис ${var:-def}будет оцениваться либо текущим значением $var, либо указанным значением по умолчанию (в данном случае def), если переменная не установлена ​​или равна нулю. Значение переменной (если есть) не изменяется.

Синтаксис ${var?message}завершится с кодом ошибки 1 и сообщением, messageесли $varон не установлен или равен нулю.

Если вам явно нужны test-совместимые тесты, вы можете сделать это:

[ ! -z "$OldIP" -a "$OldIP" != "$StartIP" ] && echo "OK"

Связанный контент