Як відкласти змінну розширення


19

Я хотів ініціалізувати деякі рядки у верхній частині мого сценарію зі змінними, які ще не встановлені, наприклад:

str1='I went to ${PLACE} and saw ${EVENT}'
str2='If you do ${ACTION} you will ${RESULT}'

а потім на PLACE, EVENT, ACTIONі RESULTбуде встановлено. Я хочу, щоб потім могла друкувати свої рядки із розширеними змінними. Це мій єдиний варіант eval? Це, здається, працює:

eval "echo ${str1}"

це стандарт? чи є кращий спосіб це зробити? Було б непогано не запускати, evalвраховуючи, що змінні можуть бути чим завгодно.

Відповіді:


23

За допомогою виду введення, який ви показуєте, єдиний спосіб використовувати розширення оболонки для заміщення значень у рядок - це використовувати evalв якійсь формі. Це безпечно, якщо ви керуєте значенням str1і можете переконатися, що воно посилається лише на змінні, які відомі як безпечні (не містять конфіденційних даних) і не містять жодного іншого спеціального символу оболонки без котирування. Ви повинні розширити рядок в подвійних лапках або в документі тут, що шлях тільки "$\`спеціальні (вони повинні передувати \в str1).

eval "substituted=\"$str1\""

Було б набагато надійніше визначити функцію замість рядка.

fill_template () {
  sentence1="I went to ${PLACE} and saw ${EVENT}"
  sentence2="If you do ${ACTION} you will ${RESULT}"
}

Встановіть змінні, а потім викликайте функцію fill_templateдля встановлення вихідних змінних.

PLACE=Sydney; EVENT=fireworks
ACTION='not learn from history'; RESULT='have to relive history'
fill_template
echo "During my holidays, $sentence1."
echo "Cicero said: \"$sentence2\"."

2
Хороша робота з використанням функції для затримки оцінки та уникнення явного eval-дзвінка.
Клейтон Стенлі

Гарне рішення, це мені дуже допомогло. Спасибі!
Стюарт

8

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

Це правда, @Gilles підходить дуже близько, але він не займається проблемою можливо переважаючих значень та способом їх використання, якщо вони вам потрібні не один раз. Зрештою, шаблон повинен використовуватися не один раз, правда?

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

ТОП

Тут ви встановите деякі параметри за замовчуванням і підготуєтесь до друку, коли їх буде викликано ...

#!/bin/sh
    _top_of_script_pr() ( 
        IFS="$nl" ; set -f #only split at newlines and don't expand paths
        printf %s\\n ${strings}
    ) 3<<-TEMPLATES
        ${nl=
}
        ${PLACE:="your mother's house"}
        ${EVENT:="the unspeakable."}
        ${ACTION:="heroin"}
        ${RESULT:="succeed."}
        ${strings:="
            I went to ${PLACE} and saw ${EVENT}
            If you do ${ACTION} you will ${RESULT}
        "}
    #END
    TEMPLATES

СРЕДНІЙ

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

    EVENT="Disney on Ice."
    _more_important_function() { #...some logic...
        [ $((1+one)) -ne 2 ] && ACTION="remedial mathematics"
            _top_of_script_pr
    }
    _less_important_function() { #...more logic...
        one=2
        : "${ACTION:="calligraphy"}"
        _top_of_script_pr
    }

BOTTOM

Зараз у вас все налаштовано, тому ось, де ви будете виконувати та виводити свої результати.

    _less_important_function
    : "${PLACE:="the cemetery"}" 
    _more_important_function
    : "${RESULT:="regret it."}" 
    _less_important_function    

РЕЗУЛЬТАТИ

Я розберуся чомусь, але запустивши вище, можна отримати такі результати:

_less_important_function()'s перший запуск:

Я зайшов до будинку вашої матері і побачив Діснея на льоду.

Якщо ви зробите каліграфію, ви досягнете успіху.

потім _more_important_function():

Я пішов на кладовище і побачив Діснея на льоду.

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

_less_important_function() знову:

Я пішов на кладовище і побачив Діснея на льоду.

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

ЯК ЦЕ ПРАЦЮЄ:

Ключовою особливістю тут є концепція conditional ${parameter} expansion.Ви можете встановити змінну до значення лише у тому випадку, якщо вона не встановлена ​​чи недійсна, використовуючи форму:

${var_name: =desired_value}

Якщо замість цього ви хочете встановити лише незмінену змінну, ви опустите значення :colonта null, які залишаться такими, які є.

НА ОБЛАСТІ:

Ви можете помітити , що в наведеному вище прикладі $PLACEі $RESULTпереодягатися при установці з допомогою , parameter expansionнавіть якщо _top_of_script_pr()вже був викликаний, імовірно , встановивши їх , коли він запущений. Причина цього працює в тому, що _top_of_script_pr()це ( subshelled )функція - я вклав її, parensа не { curly braces }використовував для інших. Оскільки він викликається в підклітині, кожна змінна, яку він встановить, є, locally scopedі коли вона повертається до батьківської оболонки, ці значення зникають.

Але коли _more_important_function()встановлено $ACTIONце globally scopedтак, це впливає на _less_important_function()'sдругу оцінку, $ACTIONоскільки _less_important_function()встановлює $ACTIONлише через${parameter:=expansion}.

:НУЛЬ

І чому я використовую провідний :colon?Ну, на manсторінці буде сказано, що : does nothing, gracefully.Ви бачите, parameter expansionсаме так воно і звучить - це expandsзначення Значення. ${parameter}.Отже, коли ми встановлюємо змінну, ${parameter:=expansion}ми залишаємо її значення - яке оболонка буде намагання виконати в онлайні. Якщо б його спробували запустити, the cemeteryвін би просто виплюнув на вас деякі помилки. PLACE="${PLACE:="the cemetery"}"дав би ті самі результати, але в цьому випадку це також зайве, і я вважав за краще оболонку: ${did:=nothing, gracefully}.

Це дозволяє вам зробити це:

    echo ${var:=something or other}
    echo $var
something or other
something or other

ТУТ-ДОКУМЕНТИ

І до речі - саме рядкове визначення нульової чи невідомою змінної також є причиною наступного:

    <<HEREDOC echo $yo
        ${yo=yoyo}
    HEREDOC
yoyo

Найкращий спосіб подумати про те here-document , як фактичний файл, який передається у вхідний файл-дескриптор. Так чи інакше вони є, але різні оболонки реалізують їх дещо інакше.

У будь-якому випадку, якщо ви не цитуєте, <<LIMITERви отримуєте його в потоковому режимі та оцінюєте для expansion.So, декларування змінної у документі here-documentможе працювати, але лише через те, expansionщо обмежує встановлення лише змінних, які вже не встановлені. Однак це абсолютно відповідає вашим потребам так, як ви їх описали, оскільки значення за замовчуванням завжди будуть встановлені під час виклику функції друку шаблону.

ЧОМУ НІ eval?

Що ж, приклад, який я представив, пропонує безпечний та ефективний спосіб прийняття, parameters.оскільки він обробляє область, кожна змінна, що знаходиться в наборі через ${parameter:=expansion}, визначається ззовні. Отже, якщо ви помістите все це в сценарій під назвою template_pr.sh і запустили:

 % RESULT=something_else template_pr.sh

Ви отримаєте:

Я зайшов до будинку вашої матері і побачив Діснея на льоду

Якщо ви зробите каліграфію, ви будете щось_забагато

Я пішов на кладовище і побачив Діснея на льоду

Якщо ви займаєтеся корекційною математикою, ви будете щось цікавіти

Я пішов на кладовище і побачив Діснея на льоду

Якщо ви займаєтеся корекційною математикою, ви будете щось цікавіти

Це не буде працювати для тих змінних , які були буквально встановлені в сценарії, такі як $EVENT, $ACTION,і , $one,але я тільки певних тем , таким чином , щоб продемонструвати різницю.

У будь-якому випадку, прийняття невідомого вводу у evaledвиписку за своєю суттю є небезпечним, тоді parameter expansionяк розроблено спеціально для цього.


1

Ви можете використовувати заповнювачі для шаблонів рядків замість нерозширених змінних. Це досить швидко забрудниться. Якщо те, що ви робите, дуже важке для шаблонів, ви можете розглянути мову з реальною бібліотекою шаблонів.

format_template() {
    changed_str=$1

    for word in $changed_str; do
        if [[ $word == %*% ]]; then
            var="${word//\%/}"
            changed_str="${changed_str//$word/${!var}}"
        fi
    done
}

str1='I went to %PLACE% and saw %EVENT%'
PLACE="foo"
EVENT="bar"
format_template "$str1"
echo "$changed_str"

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

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