Tạo khuôn mẫu với Linux trong Shell Script?


26

những gì tôi muốn hoàn thành là:

1.) Có tệp cấu hình làm mẫu, với các biến như $ version $ path (ví dụ: apache config)

2.) Có tập lệnh shell "điền" các biến của mẫu và ghi tệp được tạo vào đĩa.

Điều này có thể với một kịch bản shell. Tôi sẽ rất cảm ơn nếu bạn có thể đặt tên cho một số lệnh / công cụ tôi có thể thực hiện điều này hoặc một số liên kết tốt.

Câu trả lời:


23

Điều này là rất có thể. Một cách rất đơn giản để thực hiện điều này sẽ là cho tệp mẫu thực sự là tập lệnh và sử dụng các biến shell như

#! /bin/bash
version="1.2.3"
path="/foo/bar/baz"
cat > /tmp/destfile <<-EOF
here is some config for version $version which should
also reference this path $path
EOF

Bạn thậm chí có thể làm cho cấu hình này trên dòng lệnh bằng cách chỉ định version=$1path=$2, vì vậy bạn có thể chạy nó như thế nào bash script /foo/bar/baz 1.2.3. Các -trước EOF gây khoảng trắng trước khi dòng bị bỏ qua, sử dụng đơn giản <<EOFnếu bạn không muốn hành vi đó.

Một cách khác để làm điều này là sử dụng chức năng tìm kiếm và thay thế của sed

#! /bin/bash
version="1.2.3"
path="/foo/bar/baz"
sed -e "s/VERSION/$version/g" -e "s/PATH/$path/" /path/to/templatefile > /tmp/destfile

mà sẽ thay thế từng phiên bản của chuỗi VERSION và PATH. Nếu có những lý do khác, các chuỗi đó sẽ nằm trong tệp mẫu, bạn có thể thực hiện tìm kiếm và thay thế là VERSION hoặc% VERSION% hoặc một cái gì đó ít có khả năng được kích hoạt một cách tình cờ.


18

Không có công cụ cần thiết nào khác ngoài /bin/sh. Cho một tệp mẫu của mẫu

Version: ${version}
Path: ${path}

hoặc thậm chí với mã vỏ hỗn hợp bao gồm

Version: ${version}
Path: ${path}
Cost: ${cost}\$
$(i=1; for w in one two three four; do echo Param${i}: ${w}; i=$(expr $i + 1); done)

và một tệp cấu hình có thể phân tích cú pháp shell như

version="1.2.3-r42"
path="/some/place/under/the/rainbow/where/files/dance/in/happiness"
cost="42"

việc mở rộng này thành một vấn đề đơn giản

Version: 1.2.3-r42
Path: /some/place/under/the/rainbow/where/files/dance/in/happiness
Cost: 42$
Param1: one
Param2: two
Param3: three
Param4: four

Thật vậy, với đường dẫn đến tệp cấu hình trong biến shell config_filevà đường dẫn đến tệp mẫu trong template_file, tất cả những gì bạn cần làm là:

. ${config_file}
template="$(cat ${template_file})"
eval "echo \"${template}\""

Điều này có lẽ đẹp hơn so với việc có tập lệnh shell hoàn chỉnh dưới dạng tệp mẫu (giải pháp của @ mtinberg).

Chương trình mở rộng mẫu ngây thơ hoàn chỉnh:

#!/bin/sh

PROG=$(basename $0)

usage()
{
    echo "${PROG} <template-file> [ <config-file> ]"
}

expand()
{
    local template="$(cat $1)"
    eval "echo \"${template}\""
}

case $# in
    1) expand "$1";;
    2) . "$2"; expand "$1";;
    *) usage; exit 0;;
esac

Điều này sẽ tạo ra sự mở rộng thành đầu ra tiêu chuẩn; chỉ cần chuyển hướng đầu ra tiêu chuẩn sang một tệp hoặc sửa đổi ở trên theo cách rõ ràng để tạo ra tệp đầu ra mong muốn.

Hãy cẩn thận: Mở rộng tệp mẫu sẽ không hoạt động nếu tệp chứa dấu ngoặc kép không thoát ( "). Vì lý do bảo mật, có lẽ chúng ta nên bao gồm một số kiểm tra độ rõ ràng hoặc thậm chí tốt hơn, thực hiện chuyển đổi thoát vỏ nếu tệp mẫu được tạo bởi thực thể bên ngoài.


1
Thật tuyệt khi vẫn thấy mọi người tích cực sử dụng các kịch bản shell để làm những thứ thú vị như thế này. Một người bạn của tôi đã viết một chiếc đồng hồ trong kịch bản shell. github.com/tablespoon/fun/blob/master/cli-clock Thời gian vui vẻ thực sự.
gà con

2
Khi tôi tìm thấy câu trả lời này khi tìm cách để làm điều này bản thân mình, tôi thực hiện cách tiếp cận khá ngây thơ và đăng nó trên Github: github.com/nealian/bash-unsafe-templates
Pyrocater

9

Cách dễ nhất để làm điều này đơn giản trong Linux CLI là sử dụng envsubstvà Biến môi trường.

Tệp mẫu ví dụ apache.tmpl:

<VirtualHost *:${PORT}>
    ServerName ${SERVER_NAME}
    ServerAlias ${SERVER_ALIAS}
    DocumentRoot "${DOCUMENT_ROOT}"
</VirtualHost>

Chạy envsubstvà xuất kết quả cho tệp mới my_apache_site.conf:

export PORT="443"
export SERVER_NAME="example.com"
export SERVER_ALIAS="www.example.com"
export DOCUMENT_ROOT="/var/www/html/"
envsubst < apache.tmpl > my_apache_site.conf

Đầu ra:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot "/var/www/html/"
</VirtualHost>

2
Đây là một phần của GNU gettext, và do đó có sẵn trên tất cả các nền tảng Unix chính (bao gồm macOS X), cũng như Microsoft Windows.
tricasse

4

Bạn có lẽ nên xem xét một hệ thống quản lý cấu hình như Puppet hoặc Chef . Đây có thể dễ dàng làm những gì bạn mô tả ở trên và nhiều hơn nữa.


1
cảm ơn. Hoàn toàn, tôi đã cài đặt Chef và chạy. Nhưng nó thêm rất nhiều chi phí, khi bạn phải viết sách dạy nấu ăn của riêng bạn. Tôi không biết ngôn ngữ lập trình ruby ​​và kết luận của tôi là. dễ dàng hơn để làm điều này với một kịch bản shell cho các trường hợp dễ dàng hơn (nếu có thể).
Markus

Có vẻ như PuppetChef đều sử dụng ERB cho các mẫu và điều đó thật dễ dàng để bắt đầu. Đưa ra một biến name, chuỗi <%= name %>trong mẫu sẽ được thay thế bằng namegiá trị của. nameRõ ràng cách bạn xác định bên ngoài mẫu khác nhau giữa hai hệ thống.
Mike Renfro

1
Có templating (Với đầu bếp) là hoàn toàn dễ dàng. Nhưng sử dụng đầu bếp làm Framework (và viết sách dạy nấu ăn) đòi hỏi rất nhiều thời gian. Để lấy dữ liệu vào các mẫu, bạn cần hiểu vị trí và cách thức Chef quản lý việc "hợp nhất" các nguồn dữ liệu và rất nhiều thứ khác. Tôi đã bắt đầu viết sách dạy nấu ăn của riêng mình, nhưng một kịch bản vỏ trong trường hợp đặc biệt của tôi sẽ nhanh hơn 100 lần ...
Markus

Có được cơ sở hạ tầng cho Chef hoặc Puppet có thể là một nỗi đau hoặc bạn có thể cố gắng tìm ra cách để chạy chúng không đầu, đó là một cuộc phiêu lưu thú vị. Ansible hoạt động tốt trong chế độ kéo hoặc đẩy ra khỏi hộp để nó có thể đạt được sự cân bằng tốt hơn trong việc tìm ra nó và tự viết kịch bản. docs.ansible.com/template_module.html
gà con

4

Nếu bạn muốn các mẫu nhẹ và thực chứ không phải mã shell tạo tệp mới, các lựa chọn thông thường là sed& awkhoặc perl. Đây là một liên kết: http://savvyadmin.com/generate-text-from-temsheet-scripts-and-csv-data/

Tôi, tôi sẽ sử dụng một ngôn ngữ thực sự như perl, tcl, python, ruby ​​hoặc một cái gì đó khác trong lớp đó. Một cái gì đó được xây dựng cho kịch bản. Họ đều có các công cụ tạo khuôn mẫu tốt, đơn giản và hàng tấn ví dụ trong google.


4

Tôi sử dụng shtpl cho điều đó. (dự án riêng của tôi, có nghĩa là, nó không được sử dụng rộng rãi. Nhưng có lẽ bạn vẫn muốn thử nghiệm nó)

Ví dụ: bạn muốn tạo / etc / network / giao diện từ tệp csv, bạn có thể làm như vậy:

Nội dung tệp CSV (tại đây test.csv):

eth0;10.1.0.10;255.255.0.0;10.1.0.1
eth1;192.168.0.10; 255.255.255.0;192.168.0.1

Mẫu (ở đây giao diện.tpl):

#% IFS=';'
#% while read "Val1" "Val2" "Val3" "Val4"; do
auto $Val1 
iface $Val1 inet static
  address $Val2 
  netmask $Val3 
  gateway $Val4 

#% done < "$CSVFILE"

Chỉ huy:

$ CSVFILE=test.csv sh -c "$( shtpl interfaces.tpl )"

Kết quả:

auto eth0 
iface eth0 inet static
  address 10.1.0.10 
  netmask 255.255.0.0 
  gateway 10.1.0.1 

auto eth1 
iface eth1 inet static
  address 192.168.0.10 
  netmask  255.255.255.0 
  gateway 192.168.0.1

Thưởng thức!


1

Tôi đã cải thiện câu trả lời của FooF để người dùng không cần bỏ dấu ngoặc kép theo cách thủ công:

#!/bin/bash
template="$(cat $1)"
template=$(sed 's/\([^\\]\)"/\1\\"/g; s/^"/\\"/g' <<< "$template")
eval "echo \"${template}\""

1

Gần đây tôi đã xuất bản một tập lệnh bash hoàn thành việc đó bằng cách sử dụng cú pháp khuôn mẫu giống như jinja. Nó được gọi là cookie . Đây là bản demo:

thử nghiệm cookie


Làm thế nào để trả lời câu hỏi này?
RalfFriedl

1
@RalfFriedl Tôi không chắc ý của bạn là gì. Tôi vừa đọc lại câu hỏi nghĩ rằng tôi đã bỏ lỡ điều gì đó, nhưng tôi không thấy nó. Trong thực tế, làm thế nào điều này không trả lời bất kỳ phần nào của câu hỏi đã được hỏi? Nó gần giống như tôi đã xây dựng cookie với mục đích duy nhất là trả lời câu hỏi này ... điều mà tôi đã làm, mặc dù lúc đó tôi có thể đã nói nó hơi khác một chút. :)
Bryan Bugyi

Tôi hơi muộn để chơi hòa giải, nhưng có lẽ Ralf chỉ từ chối nhắc bạn giải thích cách cookie trả lời câu hỏi, thay vì cố gắng ám chỉ bạn không biết rõ dự án của mình đủ để đánh giá liệu nó có thể giải quyết vấn đề của ai đó không .
abathur

0

Tôi có lẽ đến trễ bữa tiệc này. Tuy nhiên, tôi đã vấp phải vấn đề tương tự và tôi đã chọn tạo công cụ mẫu BASH của riêng mình trong một vài dòng mã:

Hãy nói rằng bạn có điều này file.template:

# My template
## Author
 - @NAME@ <@EMAIL@>

rulestập tin này :

NAME=LEOPOLDO WINSTON
EMAIL=leothewinston\@leoserver.com

Bạn thực hiện lệnh này:

templater rules < file.template

Bạn nhận được điều này:

# My template
## Author
 - LEOPOLDO WINSTON <leothewinston@leoserver.com>

Bạn có thể cài đặt nó bằng cách:

 bpkg install vicentebolea/bash-templater

Đây là trang web của dự án


0

Để mở rộng câu trả lời tuyệt vời của @ FooF (dòng mới không định dạng trong nhận xét), bằng cách sử dụng ký tự điều khiển + heredoc, bạn có thể cho phép các ký tự tên tệp tùy ý :

template() {
    # if [ "$#" -eq 0 ] ; then return; fi # or just error
    eval "cat <<$(printf '\x04\x04\x04');
$(cat $1)
"
}

Điều này chấp nhận bất kỳ ký tự không null nào và chỉ cắt ngắn sớm nếu ^Dgặp 3 byte trên dòng riêng của chúng (thực tế không bao giờ). zsh thậm chí còn hỗ trợ các đầu cuối null, vì vậy printf '\x00\x00\x00'sẽ hoạt động. templatethậm chí hoạt động cho tên tập tin nguy hiểm như:

for num in `seq 10`; do
    template 'foo "$ .html' # works
done

Các mẫu shell coi chừng có thể "mở rộng" các lệnh tùy ý, vd $(launch_nukes.sh --target $(curl -sL https://freegeoip.app/csv/ | cut -d, -f 9,10)). Với sức mạnh tuyệt vời

Chỉnh sửa: nếu bạn thực sự không muốn các tệp của mình khởi chạy các hạt nhân vào bạn, chỉ cần sudo -u nobody sh(hoặc một số người dùng an toàn khác) trước.

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.