2012-05-22 3 views
7

Я написал библиотеку регистрации bash, которая будет реализована с помощью некоторых сложных сценариев, которые моя компания использует в настоящее время. Я делал ошибку при предоставлении имени файла сценария ($ {BASH_SOURCE}) и номера строки ($ {LINENO}) вызывающего скрипта при выполнении вызовов журнала. Однако я не хотел полагаться на пользователя или реализацию скрипта для передачи этих двух переменных в качестве параметров. Если бы это был C/C++, я бы просто создал макрос, который добавляет «__FILE__» и «__LINE__» в список параметров.Котировки параметра Bash и eval

Я, наконец, смог получить эту часть работы. Вот некоторые столь упрощенные тезисы как доказательство концепции:

Вот библиотека протоколирования:

# log.sh 

LOG="eval _log \${BASH_SOURCE} \${LINENO}" 

_log() { 
    _BASH_SOURCE=`basename "${1}"` && shift 
    _LINENO=${1} && shift 

    echo "(${_BASH_SOURCE}:${_LINENO}) [email protected]" 
} 

и исполнитель сценарий тестирования:

# MyTest.sh 

. ./log.sh 

${LOG} "This is a log message" 
# (test.sh:5) This is a log message 

Это работает очень хорошо (и я был взволнован, чтобы заставить его работать сначала). Однако у этого есть одна вопиющая проблема: взаимодействие между кавычками и eval. Если я позвоню:

${LOG} "I'm thrilled that I got this working" 
# ./test.sh: eval: line 5: unexected EOF while looking for matching `'' 
# ./test.sh: eval: line 6: syntax error: unexpected end of file 

Теперь я считаю, что понимаю, почему это происходит. Приведенные параметры сохраняются неизменными, поскольку они передаются в eval, но в этот момент содержимое помещается как есть в итоговую командную строку. Я знаю, что могу исправить это, сделав некоторое ускользание; однако, я не хочу, чтобы сценарии реализации должны были это делать. Прежде чем я реализовал эту способность «eval macro», у меня были пользователи, вызывающие вызовы непосредственно на «_log» и разрешившие им опционально передать «$ {LINENO}». С этой реализацией неудачный вызов выше (только с цитируемым предложением) работал нормально.

На самом базовом уровне, все, что я действительно хочу, чтобы сценарий, чтобы иметь возможность вызвать [войти функции/макрос] «Строка для входа со специальной characers» и имеет результирующее сообщение журнала содержит имя файла и строку номер вызывающего скрипта, за которым следует сообщение журнала. Если это возможно, я бы предположил, что я очень близко, но если есть что-то, что я упускаю из виду, это потребует другого подхода, я тоже к этому открыт. Я не могу заставить пользователей избегать всех своих сообщений, поскольку это, вероятно, заставит их не использовать эту библиотеку. Это такая проблема, что, если я не могу найти решение этого, я, скорее всего, вернусь к старой возможности (которая требует, чтобы $ {LINENO} передавался как параметр функции - это было гораздо менее навязчивым).

TLDR: Есть ли способ получить знак eval для соблюдения специальных символов в указанном параметре, без необходимости их избегать?

+0

Если вы используете конкретные конструкции bash, ваш сценарий ведения журнала не должен называться «log.sh». Это должно быть «log.bash». –

+0

@WilliamPursell - Я сделал некоторые поиски и не смог найти какой-либо стандарт в отношении соглашений об именах сценариев оболочки. Фактически, большинство людей, похоже, вообще не имеют суффикса. Честно говоря, я никогда не видел ничего, кроме «\ *. Sh» (или скрипта без расширения/суффикса). И во всей практичности это не должно иметь никакого влияния, верно? Интерпретатор либо извлекается из первой строки скрипта, либо используется ваша текущая оболочка. Похоже, это по сути сводится к личным предпочтениям. Но, возможно, «\ *. Sh» может означать пользователя, что сценарий использует «sh». – LousyG

+1

Единственное практическое воздействие было бы, если разработчик использует ваши функции с другой оболочкой. С именем '* .sh' разработчик разумно предположил бы, что ваша библиотека может быть получена в zsh-скрипте. –

ответ

12

Рекомендую избегать eval, если это возможно. Для вашего случая использования журналов вы можете взглянуть на встроенную оболочку caller. Если вам нужна дополнительная информация, вы можете использовать переменные BASH_SOURCE, BASH_LINENO и FUNCNAME. Обратите внимание, что все эти переменные являются массивами и содержат полный стек вызовов.Смотрите следующий пример:

#! /bin/bash  

function log() { 
    echo "[$(caller)] $*" >&2 
    echo "BASH_SOURCE: ${BASH_SOURCE[*]}" 
    echo "BASH_LINENO: ${BASH_LINENO[*]}" 
    echo "FUNCNAME: ${FUNCNAME[*]}" 
} 

function foobar() { 
    log "failed:" "[email protected]" 
} 

foobar "[email protected]" 
+1

Я никогда не думал, что это массивы! Если бы я знал это, я бы давно придумал другое решение ... Звонок довольно близок к тому, что я хочу, хотя я ничтожный и, вероятно, предпочитаю ['filename': 'line']. Тем не менее, я, безусловно, мог бы жить с тем, что у вызывающего. ОДНАКО, поскольку эти встроенные переменные являются всеми массивами, у меня есть (как вы сказали) моя трассировка стека для работы. Я должен быть в состоянии вытащить необходимую информацию оттуда внутри функции. Это позволит мне вернуться к вызовам функций вместо переменных макросов. Спасибо!!! – LousyG

+1

Просто обратите внимание: если вы хотите контролировать форматирование по имени файла: line, вы можете использовать вызывающий объект в назначении массива и использовать имя файла и строку отдельно: local fl = ($ (caller)); echo "в $ (fl [1]): $ (fl [0])" – Floyd

2

(Примечание: это решает непосредственную проблему цитирования, но @ nosid Ответит о доступе стек вызовов намного лучше)

Изменить ваше определение _log немного, чтобы прочитать со стандартного ввода вместо принимает журнальное сообщение от позиционных параметров:

_log() { 
    # Set up _BASH_SOURCE and _LINENO the same way 

    cat <(echo -n "$(_BASH_SOURCE:$_LINENO) ") - 
} 

Затем передать ваше сообщение журнала, используя здесь документ или здесь строку с помощью стандартного ввода:

${LOG} <<<"This is a log message" 
${LOG} <<<"I'm thrilled this works, too!" 
${LOG} <<HERE 
Even this long 
message works as 
intended! 
HERE 
+0

Спасибо за ввод! Я, вероятно, займусь решением nosid, так как я видел, как разработчики жаловались на что-то столь же незначительное, как «<<<». Но, приятно знать, что есть решение с меньшим воздействием, если оно мне понадобится. – LousyG

Смежные вопросы