Shell: использование функции с параметрами в if

8

Я пытаюсь выполнить код ниже, но когда я пытаюсь использовать свою функцию в операторе if, я получаю -bash: [: too many argumentsошибку. Почему это происходит?

Заранее спасибо!

notContainsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 1; done
  return 0
}

list=( "pears" "apples" "bananas" "oranges" )
blacklist=( "oranges" "apples" )
docheck=1

for fruit in "${list[@]}"
do
    if [ notContainsElement "$fruit" "${blacklist[@]}" -a $docheck = 1 ]
    then
        echo $fruit
    fi
done
Андреа Сильвестри
источник
1
Используйте shellcheck.net (или его автономную версию). Он находит проблему, упомянутую в ответах, хотя и с гораздо менее подробным объяснением и без решения.
Дэвид Фёрстер

Ответы:

14

При использовании if [ ... ]вы фактически используете [утилиту (которая аналогична, testно требует, чтобы последний аргумент был ]).

[не понимает, чтобы запустить вашу функцию, он ожидает строки. К счастью, здесь вам вообще не нужно использовать [(по крайней мере, для этой функции):

if [ "$docheck" -eq 1 ] && notContainsElement "$fruit" "${blacklist[@]}"; then
  ...
fi

Обратите внимание, что я также сначала проверяю целое число, чтобы мы могли вообще не вызывать функцию, если $docheckне равен 1.

Это работает, потому что ifпринимает произвольную команду и решает, что делать из состояния выхода этой команды. Здесь мы используем [ ... ]тест вместе с вызовом вашей функции с &&промежуточным созданием составной команды. Состояние выхода составной команды будет истинным, если и [ ... ]тест, и функция вернули ноль в качестве своих состояний выхода, сигнализируя об успехе.

Как примечание стиля, у меня не было бы функционального теста, если массив не содержит элемент, но если ли содержит элемент, и затем

if [ "$docheck" -eq 1 ] && ! contains "$fruit" "${blacklist[@]}"; then ...

Имея тестовую функцию отрицательный испортит логику в тех случаях , когда вы делаете хотите проверить , содержит ли массив элемента ( if ! notContainsElement ...).

Кусалананда
источник
Большое спасибо за ответ и другие советы!
Андреа Сильвестри
3

пытаться

if notContainsElement "$fruit" "${blacklist[@]}" && test "$docheck" = 1
then
  • -aопция не является ни оболочкой, ни testопцией

здесь у вас есть две части теста

notContainsElement "$fruit" "${blacklist[@]}"
test $docheck = 1 ## or [ $docheck = 1 ]

Вы связываете тогда, ifиспользуя

if cmd1 && cmd2

как указано выше, -aэто тестовый вариант, но он может использоваться только с другим тестовым вариантом, поэтому вы можете использовать

if [ "$a" -lt "$b" -a "$a" -lt "$c" ]

чтобы проверить, что $aниже, чем оба $bи $c, но вы не можете использовать другие команды в рамках теста.

Archemar
источник
arg, @Kusalananda снова стал самым быстрым оружием в мире дикой паутины;)
Archemar
RSI - это цена, которую я плачу за это.
Кусалананда
-aЭто тестовый вариант, это означает, что И.
Гайд
@hyde За исключением того, что он был помечен как устаревший в стандарте POSIX.
Кусалананда
1
@Archemar, ... подумайте над исправлением ошибок цитирования, которые все еще присутствуют в ваших примерах (без кавычек $docheck, без кавычек $a/ $b/ etc в последнем примере).
Чарльз Даффи
0

Вот альтернатива, которая может не понравиться некоторым людям. Преобразуйте массив из черного списка в строку и посмотрите, совпадает ли строка с удаленным фруктом. Отредактировано, чтобы дополнить строки пробелами. Спасибо Скотту за указание на проблему с яблоками и ананасами.

badlist=" ${blacklist[@]} "
for f in "${list[@]}"
do
    if [[ "${badlist/" $f "/}" == "$badlist" ]]
    then
        echo "$f"
    fi
done

Я думаю, что это проще, но не хватает логики &&, которую многие предпочитают.

расточитель
источник
(1) Это терпит неудачу в случае, если один из listплодов содержится в одном из blacklistплодов. Например, если мы изменим blacklistна ( "oranges" "pineapples" ), вывод вашего скрипта не изменится. … (Продолжение)
Скотт
(Продолжение)… (2) Я не совсем понимаю, что вы подразумеваете под «я думаю, что это проще, но в нем отсутствует логика &&, которую предпочитают многие». Часто проще сделать вещи проще, не прибегая к функциональности. Как Альберт Эйнштейн мог сказать или не сказать: «Все должно быть сделано как можно проще, но не проще». Почему ты пропустил &&? (3) Когда вы публикуете код, сделайте соответствующий отступ.
Скотт
Ну, я пытался использовать отступ с 4 пробелами, но, похоже, это не сработало. Я понимаю, что ты имеешь в виду под ананасами.
Wastrel
Он также не может различить одну запись списка two wordsи два последующих элемента twoи words. И если бы ваш список содержался *как запись, он бы соответствовал всему. (И потому , что вы не цитируя $fв том, что должно быть echo "$f", если вы даже пытаться повторить такой элемент, он будет заменен списком имен файлов в текущем каталоге).
Чарльз Даффи
Я сделал несколько правок. Это не тривиально, когда данные в массивах произвольны. Предположим, что «яблоки» занесены в черный список, а «яблоки Гренни Смит» - это элемент другого массива. Я думаю, что код ОП имеет аналогичные проблемы. Массивы должны быть сделаны из элементов, которые «работают».
Wastrel