Làm thế nào để trì hoãn mở rộng biến


18

Tôi muốn khởi tạo một số chuỗi ở đầu tập lệnh của mình với các biến chưa được đặt, chẳng hạn như:

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

và sau đó sau này PLACE, EVENT, ACTION, và RESULTsẽ được thiết lập. Tôi muốn sau đó có thể in các chuỗi của mình ra với các biến được mở rộng. Là lựa chọn duy nhất của tôi eval? Điều này dường như làm việc:

eval "echo ${str1}"

là tiêu chuẩn này? Có cách nào tốt hơn để làm điều này? Sẽ tốt hơn nếu không chạy evalxem xét các biến có thể là bất cứ điều gì.

Câu trả lời:


23

Với loại đầu vào bạn hiển thị, cách duy nhất để tận dụng mở rộng shell để thay thế các giá trị thành một chuỗi là sử dụng evalở một số dạng. Điều này an toàn miễn là bạn kiểm soát giá trị của str1và có thể đảm bảo rằng nó chỉ tham chiếu các biến được gọi là an toàn (không chứa dữ liệu bí mật) và không chứa bất kỳ ký tự đặc biệt nào không được trích dẫn. Bạn nên mở rộng chuỗi bên trong dấu ngoặc kép hoặc trong tài liệu ở đây, cách đó chỉ "$\`đặc biệt (chúng cần được đi trước bởi dấu \trong str1).

eval "substituted=\"$str1\""

Sẽ mạnh mẽ hơn rất nhiều khi định nghĩa một hàm thay vì một chuỗi.

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

Đặt các biến sau đó gọi hàm fill_templateđể đặt các biến đầu ra.

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
Làm việc tốt bằng cách sử dụng một chức năng để trì hoãn đánh giá, và để tránh các cuộc gọi eval rõ ràng.
Clayton Stanley

Giải pháp tốt, điều này đã giúp tôi rất nhiều. Cảm ơn!
Stuart

8

Khi tôi hiểu ý của bạn, tôi không tin rằng bất kỳ câu trả lời nào trong số này là đúng. evalkhông cần thiết theo bất kỳ cách nào, bạn cũng không có nhu cầu thậm chí hai lần đánh giá các biến của mình.

Đó là sự thật, @Gilles đến rất gần, nhưng anh ta không giải quyết vấn đề có thể ghi đè các giá trị và cách sử dụng chúng nếu bạn cần chúng nhiều lần. Rốt cuộc, một mẫu nên được sử dụng nhiều lần, phải không?

Tôi nghĩ rằng đó là thứ tự mà bạn đánh giá chúng quan trọng hơn. Hãy xem xét những điều sau đây:

HÀNG ĐẦU

Tại đây bạn sẽ đặt một số giá trị mặc định và chuẩn bị in chúng khi được gọi ...

#!/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

Ở GIỮA

Đây là nơi bạn xác định các chức năng khác để gọi chức năng in của mình dựa trên kết quả của chúng ...

    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
    }

CHAI

Bạn đã có tất cả thiết lập ngay bây giờ, vì vậy đây là nơi bạn sẽ thực hiện và lấy kết quả của mình.

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

CÁC KẾT QUẢ

Tôi sẽ đi vào lý do tại sao ngay lập tức, nhưng chạy ở trên tạo ra kết quả sau:

_less_important_function()'s lần chạy đầu tiên:

Tôi đã đến nhà mẹ bạn và thấy Disney trên Ice.

Nếu bạn làm thư pháp bạn sẽ thành công.

sau đó _more_important_function():

Tôi đến nghĩa trang và thấy Disney trên Ice.

Nếu bạn làm toán khắc phục, bạn sẽ thành công.

_less_important_function() lần nữa:

Tôi đến nghĩa trang và thấy Disney trên Ice.

Nếu bạn làm toán học khắc phục, bạn sẽ hối tiếc.

LÀM THẾ NÀO NÓ HOẠT ĐỘNG:

Tính năng chính ở đây là khái niệm conditional ${parameter} expansion.Bạn chỉ có thể đặt biến thành giá trị nếu nó không được đặt hoặc null bằng cách sử dụng biểu mẫu:

${var_name: =desired_value}

Nếu thay vào đó, bạn chỉ muốn đặt một biến không đặt, bạn sẽ bỏ qua các :colongiá trị null và vẫn giữ nguyên.

TRÊN PHẠM VI:

Bạn có thể nhận thấy rằng trong ví dụ trên $PLACE$RESULTđược thay đổi khi được đặt qua parameter expansionmặc dù _top_of_script_pr()đã được gọi, có lẽ là đặt chúng khi nó chạy. Lý do công việc này _top_of_script_pr()là một ( subshelled )chức năng - Tôi đã đính kèm nó parensthay vì { curly braces }sử dụng cho các chức năng khác. Bởi vì nó được gọi trong một lớp con, mọi biến nó đặt locally scopedvà khi nó trở về vỏ mẹ, các giá trị đó sẽ biến mất.

Nhưng khi _more_important_function()thiết lập $ACTIONthì globally scopednó ảnh hưởng đến _less_important_function()'sđánh giá thứ hai $ACTIONvì chỉ _less_important_function()thiết lập $ACTIONthông qua${parameter:=expansion}.

:VÔ GIÁ TRỊ

Và tại sao tôi sử dụng :colon?Giếng hàng đầu , mantrang sẽ cho bạn biết rằng : does nothing, gracefully.Bạn thấy, parameter expansionchính xác là nó trông như thế nào - nó expandscó giá trị của ${parameter}.Vì vậy, khi chúng ta đặt một biến với ${parameter:=expansion}giá trị còn lại của chúng ta - cái vỏ sẽ cố gắng thực hiện nội tuyến. Nếu nó cố chạy, the cemeterynó sẽ chỉ nhổ một số lỗi vào bạn. PLACE="${PLACE:="the cemetery"}"sẽ tạo ra kết quả tương tự, nhưng nó cũng dư thừa trong trường hợp này và tôi thích vỏ đó: ${did:=nothing, gracefully}.

Nó cho phép bạn làm điều này:

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

TẠI ĐÂY-TÀI LIỆU

Và nhân tiện - định nghĩa nội tuyến của biến null hoặc unset cũng là lý do tại sao các hoạt động sau:

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

Cách tốt nhất để nghĩ về một here-document là một tệp thực tế được truyền đến một bộ mô tả tệp đầu vào. Ít nhiều đó là những gì chúng là, nhưng các vỏ khác nhau thực hiện chúng hơi khác nhau.

Trong mọi trường hợp, nếu bạn không trích dẫn, <<LIMITERbạn sẽ truyền nó đánh giá vì expansion.vậy việc khai báo một biến trong một here-documentcó thể hoạt động, nhưng chỉ thông qua expansionđó giới hạn bạn chỉ đặt các biến chưa được đặt. Tuy nhiên, điều đó hoàn toàn phù hợp với nhu cầu của bạn như bạn đã mô tả về chúng, vì các giá trị mặc định của bạn sẽ luôn được đặt khi bạn gọi chức năng in mẫu của mình.

TẠI SAO KHÔNG eval?

Chà, ví dụ tôi đã trình bày cung cấp một phương tiện chấp nhận an toàn và hiệu quả parameters.Bởi vì nó xử lý phạm vi, mọi biến trong tập hợp thông qua ${parameter:=expansion}đều có thể xác định được từ bên ngoài. Vì vậy, nếu bạn đặt tất cả điều này trong một tập lệnh có tên template_pr.sh và chạy:

 % RESULT=something_else template_pr.sh

Bạn sẽ nhận được:

Tôi đã đến nhà mẹ bạn và thấy Disney trên Ice

Nếu bạn làm thư pháp, bạn sẽ làm gì đó

Tôi đã đến nghĩa trang và thấy Disney trên Ice

Nếu bạn làm toán học khắc phục, bạn sẽ có một cái gì đó

Tôi đã đến nghĩa trang và thấy Disney trên Ice

Nếu bạn làm toán học khắc phục, bạn sẽ có một cái gì đó

Điều này sẽ không hoạt động đối với các biến được đặt theo đúng nghĩa đen trong tập lệnh, chẳng hạn như $EVENT, $ACTION,$one,tôi chỉ xác định các biến đó theo cách đó để chứng minh sự khác biệt.

Trong mọi trường hợp, việc chấp nhận đầu vào không xác định vào một evaledcâu lệnh vốn đã không an toàn, trong khi đó parameter expansionđược thiết kế đặc biệt để thực hiện nó.


1

Bạn có thể sử dụng trình giữ chỗ cho các mẫu chuỗi thay vì các biến chưa được mở rộng. Điều này sẽ nhận được lộn xộn khá nhanh chóng. Nếu những gì bạn đang làm là rất nặng mẫu, bạn có thể muốn xem xét một ngôn ngữ với một thư viện mẫu thực sự.

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"

Nhược điểm ở trên là biến mẫu phải là từ riêng của nó (ví dụ: bạn không thể làm được "%prefix%foo"). Điều này có thể được sửa chữa với một số sửa đổi, hoặc đơn giản bằng cách mã hóa cứng biến mẫu thay vì nó là động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.