bash :назначить переменной с помощью подоболочки, но выйти из основного сценария, если назначение не удалось

@steeldriver уже объяснил, что \bотносится к , я просто добавлю, что apt-file searchсам по себе поддерживает расширенное регулярное выражение с опцией -x, и что вы можете искать пути , заканчивающиеся в apache2.confс:

apt-file search -x 'apache2\.conf$'

Вы также можете:

apt-file search -x 'apache2\.conf(\s|$)'

Искать пути, которые заканчиваются на apache2.confили содержат apache2.conf, за которым следует пробел, но я сомневаюсь, что это будет иметь какое-либо значение, поскольку я не ожидаю, что какой-либо файл в базе данных apt-fileсодержит apache2.conf.

0
29.10.2021, 19:55
2 ответа

Выход из подоболочки не приведет к выходу из основного сценария, как вы это видели.

Я думаю о 3 (с половиной )решениях:

  1. используйте set -e, чтобы любая ("непроверенная" )неудачная команда (или подоболочка )немедленно выходили из основного сценария (это может быть излишним или вызывать другие проблемы ),
  2. отправить сигнал из функции и перехватить его с помощьюtrap
  3. используйте || exit $?после каждого VALUEn=$(...)вот так VALUE=$(require_line "myKey") || exit $?,
  4. объединить (3. )с (не очень элегантной петлей ), используя eval.

Этот третий не совсем «требует if вокруг каждого» и все равно будет довольно компактным синтаксисом IMHO.


Кстати , эта строчка

echo "Key not found in $DATA, key: $KEY"

...на самом деле бесполезно, если вы выйдете из всего скрипта сразу после этого, потому что предложение будет сохранено в переменной $VALUEn, которая не будет отображаться.

Предлагаю печатать на stderrвот так:

echo "my error" 1>&2

Примеры

Пример решения 1

#!/bin/sh
set -e

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then exit 1 ; fi
}

VALUE1=$(myfunc "OK") 
echo $VALUE1
VALUE2=$(myfunc "NO WAY") 
echo $VALUE2

echo "main script did not exit"
$./test.sh
OK
zsh: exit 1    ./test.sh

Но если я уберу set -eс самого начала, я получу:

$./test.sh
OK
NO WAY
main script did not exit

Пример решения 2

#!/bin/sh

trap "exit $?" USR1

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then kill -USR1 $$ ; fi 
}

VALUE1=$(myfunc "OK") 
echo $VALUE1
VALUE2=$(myfunc "NO WAY")
echo $VALUE2

echo "main script did not exit"
$./test.sh
OK

Пример решения 3

#!/bin/sh

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then exit 1 ; fi
}

VALUE1=$(myfunc "OK") || exit $?
echo $VALUE1
VALUE2=$(myfunc "NO WAY") || exit $?
echo $VALUE2

echo "main script did not exit"
$./test.sh
OK
zsh: exit 1    ./test.sh

Пример решения 4

#!/bin/sh

myfunc(){
        echo $1
        if [ "$1" != "OK" ] ; then exit 1 ; fi
}

I=1
for key in "OK" "OK" "NO WAY": ; do
        eval "VALUE$I=\$(myfunc \"$key\")" || exit $?
        eval "echo \$VALUE$I"
        I=$(($I+1))
done
echo "main script did not exit"
$./test.sh
OK
OK
zsh: exit 1    ./test.sh
1
29.10.2021, 23:12

Давайте немного изменим структуру вашего require_lineскрипта, чтобы помочь вам.

Во-первых, мы можем избавиться от бесполезного cat | grep. Во-вторых, мы можем использовать grepвнутреннее поведение, указывающее на успех или неудачу поиска KEY, а также печать ключа, если он найден, в stdout.

require_line(){
  local KEY="$1"
  local FILE="/tmp/myfile"

  if grep "$KEY" "$FILE"
  then
    return 0
  else
    printf 'Key not found in:\n\n"%s"\n\nKey: "%s"\n' "$(cat "$FILE")" "$KEY" >&2
    return 1
  fi
}

Это использует встроенный -в поведение grep. Если ключ найден, grepпечатает соответствующую строку и возвращает успех. В противном случае берется ветвь elseи выводится сообщение о том, что ключ не найден. Кроме того, в случае сбоя grepсообщение об ошибке печатается в stderr, чтобы сообщение об ошибке не было ошибочно принято за действительное совпадение найдено в $FILE.

Вы можете дополнительно изменить require_line, чтобы принять имя файла в качестве $2параметр, изменив строку на local FILE="$2"и затем передав нужное имя файла каждый раз, когда вы вызываете require_line.

Теперь, когда это на месте....

Вам действительно нужно хранить каждое VALUEn для KEYn, или вам просто нужно убедиться, что они все присутствуют?

Теперь, когда у вас есть функция require_line, которая точно возвращает успех или значение ошибки, вы можете просто ANDвсе ваши тесты вместе. Как только любой из них не пройден, общий тест не пройден.

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

if value1=$(require_line "key1") &&
   value2=$(require_line "key2") &&
   value3=$(require_line "key3")
then
   printf "%s\n" "$value1" "$value2" "$value3"
else
   printf "One or more keys failed.\n" >&2
fi

Это будет утомительно, если у вас есть множество ключей для проверки. Использование массива может быть лучше:

#!/usr/bin/env bash

require_line(){
  local KEY="$1"
  local FILE="/tmp/myfile" # or perhaps "$2"

  if grep "$KEY" "$FILE"
  then
    return 0
  else
    printf 'Key not found in:\n\n"%s"\n\nKey: "%s"\n' "$(cat "$FILE")" "$KEY" >&2
    return 1
  fi
}

declare keys=("this" "is" "a" "test" "\." "keyN")
N=${#keys[@]}

declare values=()

j=0
while [ $j -lt $N ] && values[$j]="$(require_line "${keys[j]}")"
do
  j=$(($j+1))
done

if [ $j -lt $N ]
then
  printf 'error: found only %d keys out of %d:\n' $j $N
  printf '  "%s"\n' "${values[@]}"
fi

Запуск этого кода с некоторыми демонстрационными данными:

$ cat /tmp/myfile
this is a test.
$./test.sh 
Key not found in:

"this is a test."

Key: "keyN"
error: found only 5 keys out of 6:
  "this is a test."
  "this is a test."
  "this is a test."
  "this is a test."
  "this is a test."
  ""

Наконец, если вам действительно нужно только убедиться, что все ключи присутствуют, без необходимости знать, каковы значения совпадения, код, ориентированный на массив -выше, может быть упрощен до простого цикла до тех пор, пока не будут найдены все ключи, или для прервать при первой обнаруженной отсутствующей клавише.

1
29.10.2021, 21:09

Теги

Похожие вопросы