Як змусити bash перервати виконання сценарію на синтаксичній помилці?


15

Щоб бути в безпеці, я б хотів, щоб bash перервав виконання сценарію, якщо він зіткнувся з синтаксичною помилкою.

На мій подив, я не можу цього досягти. ( set -eнедостатньо.) Приклад:

#!/bin/bash

# Do exit on any error:
set -e

readonly a=(1 2)

# A syntax error is here:

if (( "${a[#]}" == 2 )); then
    echo ok
else
    echo not ok
fi

echo status $?

echo 'Bad: has not aborted execution on syntax error!'

Результат (bash-3.2.39 або bash-3.2.51):

$ ./sh-on-syntax-err
./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Ну, ми не можемо перевіряти $?після кожного твердження, щоб виявити синтаксичні помилки.

(Я очікував такої безпечної поведінки від розумної мови програмування ... можливо, про це потрібно повідомити як помилку / бажання розбити розробників)

Більше експериментів

if не має значення.

Видалення if:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
(( "${a[#]}" == 2 ))
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Результат:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Можливо, це пов’язано з вправою 2 від http://mywiki.wooledge.org/BashFAQ/105 і має щось спільне (( )). Але я вважаю, що все ще нерозумно продовжувати виконувати помилку синтаксису.

Ні, (( ))не має значення!

Він поводиться погано навіть без арифметичного тесту! Просто простий базовий сценарій:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
echo "${a[#]}"
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Результат:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

set -eнедостатньо, оскільки ваша синтаксична помилка знаходиться в ifоператорі. Більше ніде слід скасувати сценарій.
Йорданм

@jordanm Добре, це може бути поясненням, чому set -eце не спрацювало. Але моє запитання все ж має сенс. Чи можливо перервати будь-яку синтаксичну помилку?
imz - Іван Захарящев

@jordanm Видалено "якщо"; не робить різниці (оновлено моє запитання).
imz - Іван Захарящев

Відповіді:


9

Згортання цілого у функцію, здається, робить трюк:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

Результат:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

Хоча я не маю поняття, чому - може, хтось ще може пояснити?


2
Тепер визначення вашої функції аналізується та оцінюється, і воно не вдається.
трійчатка

Приємне рішення! До речі, вона не скасовує всю програму і в цьому випадку. Я додав echo 'Bad2: has not aborted the execution after bad main!'як ваш останній приклад, і вихід: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: рядок 6: #: синтаксична помилка: очікується операнд ( маркер помилки "#") Bad2: не скасовував виконання після поганого основного! $
imz - Іван Захарящев

Але ми не повинні додавати рядок просто тоді, ми повинні поставити все всередині функції.
imz - Іван Захарящев

@tripleee Так, схоже, аналіз функції не вдається, тому він не завершений, але вся програма в цьому випадку фактично не переривається (тому, мабуть, це не ефект виходу з помилки, мабуть).
imz - Іван Захарящев

6

Ви, ймовірно, вводите в оману щодо справжнього значення set -e. Уважне читання результатів help setшоу:

-e  Exit immediately if a command exits with a non-zero status.

Отже -e, про те, що статус виходу команд є ненульовим, а не про синтаксичні помилки у вашому сценарії.

Взагалі вважається поганою практикою використання set -e, тому що всі помилки (тобто всі ненульові повернення з команд) повинні спритно оброблятися сценарієм (подумайте, надійний сценарій, а не ті, що збуваються після введення імені файлу з простір або те, що починається з гіпена).

Залежно від типу синтаксичної помилки, сценарій може взагалі не виконуватися. Я недостатньо обізнаний в баші, щоб точно сказати , який клас синтаксичних помилок (якщо тільки їх можна класифікувати) може призвести до негайного переривання сценарію чи ні. Можливо, якийсь гурт Баша приєднається і все прояснить.

Я тільки сподіваюся, що я уточнив set -eзаяву!

Про ваше бажання:

Я очікував такої безпечної поведінки від розумної мови програмування ... можливо, про це треба повідомити як про помилку / бажання забути розробників

Відповідь, безумовно, ні! як те, що ви спостерігали ( set -eне реагуючи так, як ви очікували) насправді дуже добре зафіксовано.


Я мав на увазі відсутність такої функції - проблема. Я не хотів зосереджуватися на цьому set -e- це лише трохи наближене до моїх цілей, тому це згадується та використовується тут. Моє запитання не про set -eце, це про незахищеність bash, якщо його не можна зробити для відмови від ситаксичних помилок. Я шукаю спосіб змусити його завжди переривати синтаксичні помилки.
imz - Іван Захарящев

4

Ви можете змусити перевірити сценарій, поставивши щось на кшталт

bash -n "$0"

у верхній частині сценарію - після, set -eале перед будь-яким суттєвим фрагментом коду.

Я мушу сказати, що це не дуже надійно, але якщо це працює для вас, можливо, це прийнятно.


Відмінно! Або без set -e: bash -n "$0" || exit
Daniel S

0

По-перше, (( ))в bash використовується в якості арифметичних обчислень, а не для використання в if ... використовувати []для цього.

По-друге, ${a[#]}дивно, і саме тому помилки ... #не має значення масиву

Я не знаю, що ви хочете зробити з цим, але я припускаю, що ви хочете знати кількість полів, тому ви хочете ${#a[*]}замість цього

Нарешті, при порівнянні цілих чисел значення -eqрекомендується перевищувати ==(використовується для рядків). ==також буде працювати, але -eqрекомендується.

так що ви хочете:

if [ ${#a[*]} -eq 2 ]; then 

4
Що це не так. Загальним є використання ((ключового слова з ifключовим словом. Наприклад, if (( 5 * $b > 53 )). Якщо ви прагнете до переносимості зі старими снарядами, [[не є в цілому краще більш [.

2
Так, я погоджуюся з @Evan - [[і ((були розроблені спеціально як легкі тести, які використовуються з "якщо" тощо, на відміну від них [, вони ніколи не породили підпроцес для оцінки стану.
imz - Іван Захарящев

1
[являє собою bash вбудований. Проте старші снаряди очікують, що це буде власна програма.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.