Получение неправильного $LINENO для захваченной функции

Каждый процесс имеет ряд переменных среды (который может быть установлен в оболочке export SOMEVAR=value и так). Они наследованы как есть дочерними процессами. Если Вы хотите сбросить их, использовать extern char **environ; достигать переданную среду (см. environ(7)), и затем используйте execle(3) или execve(3) передача новой среды выстраивает со скопированными значениями переменных. Некоторое руководство на том, что и то, как действительно делают это, дано David Wheeler в его "Безопасном ПРАКТИЧЕСКОМ РУКОВОДСТВЕ программирования Unix/Linux"

7
25.08.2014, 19:00
4 ответа

Думаю, проблема в том, что вы ожидаете, что "$ LINENO" выдаст вам строку выполнения для последнего команда, которая может почти работать, но clean_a () также получает свою собственную $ LINENO , которую вы должны использовать вместо этого:

error "something!
line: $1
...

Но даже это, вероятно, не сработает потому что я ожидаю, что он просто напечатает строку, в которой вы установили ловушку .

Вот небольшая демонстрация:

PS4='DEBUG: $LINENO : ' \
bash -x <<\CMD          
    trap 'fn "$LINENO"' EXIT             
    fn() { printf %s\\n "$LINENO" "$1"; }
    echo "$LINENO"
CMD

OUTPUT

DEBUG: 1 : trap 'fn "$LINENO"' EXIT
DEBUG: 3 : echo 3
3
DEBUG: 1 : fn 1
DEBUG: 2 : printf '%s\n' 2 1
2
1

Итак, ловушка устанавливается, затем определяется fn () , затем echo выполнен. Когда оболочка завершает выполнение своего ввода, запускается ловушка EXIT и вызывается fn . Ему передается один аргумент - trap строка $ LINENO . fn сначала выводит свой собственный $ LINENO , а затем свой первый аргумент.

Я могу придумать один способ добиться ожидаемого поведения, но он как бы портит stderr оболочки:

PS4='DEBUG: $((LASTNO=$LINENO)) : ' \
bash -x <<\CMD
    trap 'fn "$LINENO" "$LASTNO"' EXIT
    fn() { printf %s\\n "$LINENO" "$LASTNO" "$@"; }
    echo "$LINENO"
CMD

ВЫХОД

DEBUG: 1 : trap 'fn "$LINENO" "$LASTNO"' EXIT
DEBUG: 3 : echo 3
3
DEBUG: 1 : fn 1 3
DEBUG: 2 : printf '%s\n' 2 1 1 3
2
1
1
3

Он использует $ PS4 оболочки. ] приглашение отладки для определения $ LASTNO в каждой выполняемой строке. Это текущая переменная оболочки, к которой вы можете получить доступ в любом месте сценария. Это означает, что независимо от того, к какой строке в данный момент осуществляется доступ, вы можете ссылаться на самую последнюю строку запущенного скрипта в $ LASTNO . Конечно, как вы можете видеть, он идет с выводом отладки. Вы можете нажать это на 2> / dev / null , возможно, для большей части выполнения скрипта, а затем просто 2> & 1 в clean_a () или что-то в этом роде .

Причина, по которой вы получаете 1 в $ LASTNO , заключается в том, что является последним значением, на которое было установлено $ LASTNO , потому что это было последним значением $ LINENO . У вас есть ловушка в функции archieve_it () , поэтому она получает свою собственную $ LINENO , как указано в спецификации ниже. Хотя не похоже, что bash в любом случае делает там правильные вещи, так что это также может быть связано с тем, что ловушка должна повторно запустить оболочку на INT signal и $ LINENO , следовательно, сбрасывается. Я немного не уверен в этом в данном случае - как, по-видимому, bash .

Я думаю, вы не хотите оценивать $ LASTNO в clean_a () . Лучше было бы оценить его в ловушке и передать значение , которое ловушка получает в $ LASTNO , через clean_a () в качестве аргумента. . Может быть так:

#!/bin/bash
PS4='^MDEBUG: $((LASTNO=$LINENO)) : '; set -x
archieve_it () {
    trap 'clean_a $LASTNO $LINENO "$BASH_COMMAND"' \
        SIGHUP SIGINT SIGTERM SIGQUIT
    while :; do sleep 1; done
} 2>/dev/null
clean_a () { : "$@" ; } 2>&1

Попробуй - я думаю, он должен делать то, что ты хочешь. Да, и обратите внимание, что в PS4 = ^ M ^ M является буквальным возвратом, как CTRL + V ENTER.

Из спецификации оболочки POSIX :

Устанавливается оболочкой в ​​десятичное число, представляющее текущий последовательный номер строки (пронумерованный, начиная с 1) в сценарии или функции перед выполнением каждой команды. Если пользователь сбрасывает или сбрасывает LINENO , переменная может потерять свое особое значение на время жизни оболочки. Если оболочка в настоящее время не выполняет сценарий или функцию, значение LINENO не указано. Этот том IEEE Std 1003.1-2001 определяет эффекты переменной только для систем, поддерживающих опцию User Portability Utilities.

4
27.01.2020, 20:17

Решение mikeserv хорошее, но он не прав в том, что fn передается строке trap $LINENO при выполнении ловушки. Вставьте строку перед trap ... и вы увидите, что fn на самом деле всегда передается 1, независимо от того, где была объявлена ловушка.

PS4='DEBUG: $LINENO : ' \
bash -x <<\EOF
    echo Foo
    trap 'fn "$LINENO"' EXIT             
    fn() { printf %s\\n "$LINENO" "$1"; }
    echo "$LINENO"
    exit
EOF

OUTPUT

DEBUG: 1 : echo Foo
Foo
DEBUG: 2 : trap 'fn "$LINENO"' EXIT
DEBUG: 4 : echo 4
4
DEBUG: 5 : exit
DEBUG: 1 : fn 1
DEBUG: 3 : printf '%s\n' 3 1
3
1

Поскольку первый аргумент ловушки, fn "$LINENO", заключен в одинарные кавычки, $LINENO получает расширение, если и только когда EXIT сработал и поэтому должен расшириться до fn 5. Так почему же этого не происходит? На самом деле так и было, вплоть до bash-4.0, когда он был намеренно изменен таким образом, что $LINENO сбрасывается в 1, когда срабатывает ловушка, и поэтому расширяется до fn 1. [источник] Однако для ловушек ERR сохраняется оригинальное поведение, вероятно, потому что часто что-то вроде trap 'echo "Error at line $LINENO"'. ERR используется.

#!/bin/bash

trap 'echo "exit at line $LINENO"' EXIT
trap 'echo "error at line $LINENO"' ERR
false
exit 0

OUTPUT

error at line 5
exit at line 1

проверено с GNU bash, версия 4.3.42(1)-release (x86_64-pc-linux-gnu)

6
27.01.2020, 20:17

Получение LINENO = 0вместо фактического номера строки при выходе из скрипта можно исправить, перехвативERR(вместоEXIT).

Кроме того, добавьте set -E, чтобы гарантировать, что эти ловушки ERR наследуются функциями, подстановками команд и средами подоболочек. Например, вот как напечатать имя функции, вызвавшей ошибку скрипта, и номер ее строки:

set -eE
trap 'echo "Error in function $FUNCNAME at line $LINENO"' ERR

Кредиты:https://citizen428.net/blog/bash-error-handling-with-trap/

1
14.07.2020, 12:44

это должно подойти для более поздних версий bash:

trap 'debug_line_old=$debug_line;debug_line=$LINENO' DEBUG 
trap 'catch echo EXIT rc=$? line=$debug_line_old' EXIT
0
02.11.2020, 09:29

Теги

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