เทมเพลตกับ Linux ใน Shell Script หรือไม่


26

สิ่งที่ฉันอยากรู้คือ:

1. ) การมีไฟล์ปรับแต่งเป็นเทมเพลตโดยมีตัวแปรเช่น $ version $ path (เช่น apache config)

2. ) การมีเชลล์สคริปต์ที่ "เติม" ตัวแปรของเทมเพลตและเขียนไฟล์ที่สร้างไปยังดิสก์

เป็นไปได้ด้วยเชลล์สคริปต์ ฉันจะขอบคุณมากถ้าคุณสามารถตั้งชื่อคำสั่ง / เครื่องมือที่ฉันสามารถทำได้หรือเชื่อมโยงที่ดี

คำตอบ:


23

เป็นไปได้มาก วิธีที่ง่ายมากที่จะใช้สิ่งนี้จะทำให้ไฟล์เทมเพลตเป็นสคริปต์และใช้ตัวแปรเชลล์เช่น

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

คุณยังสามารถทำให้การกำหนดค่านี้ในบรรทัดคำสั่งโดยการระบุversion=$1และเพื่อให้คุณสามารถเรียกใช้มันเหมือนpath=$2 ก่อน EOF ทำให้เกิดช่องว่างก่อนที่จะสายถูกละเว้นการใช้งานธรรมดาถ้าคุณไม่ต้องการพฤติกรรมที่bash script /foo/bar/baz 1.2.3-<<EOF

อีกวิธีในการทำเช่นนี้คือการใช้การค้นหาและแทนที่การทำงานของ 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

ซึ่งจะแทนที่แต่ละอินสแตนซ์ของสตริง VERSION และ PATH หากมีสาเหตุอื่น ๆ สตริงเหล่านั้นจะอยู่ในไฟล์เทมเพลตคุณอาจทำการค้นหาและแทนที่เป็นVERSIONหรือ% VERSION% หรือบางสิ่งที่มีโอกาสน้อยที่จะถูกเรียกโดยไม่ตั้งใจ


18

ไม่มีเครื่องมือที่จำเป็นอื่น ๆ /bin/shกว่า รับไฟล์เทมเพลตของแบบฟอร์ม

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

หรือรวมกับรหัสเชลล์ผสมรวมอยู่ด้วย

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)

และไฟล์การกำหนดค่าแยกวิเคราะห์เชลล์เช่น

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

มันเป็นเรื่องง่ายที่จะขยายไปสู่

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

แน่นอนให้เส้นทางไปยังไฟล์การกำหนดค่าในตัวแปรเปลือกconfig_fileและเส้นทางไปยังไฟล์แม่แบบในtemplate_fileสิ่งที่คุณต้องทำคือ:

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

นี่อาจจะดีกว่าการมีเชลล์สคริปต์ที่สมบูรณ์เป็นไฟล์เทมเพลต (โซลูชันของ @ mtinberg)

โปรแกรมขยายเทมเพลตไร้เดียงสาที่สมบูรณ์:

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

สิ่งนี้จะเอาท์พุทการขยายตัวไปยังเอาท์พุทมาตรฐาน; เพียงแค่เปลี่ยนเส้นทางเอาต์พุตมาตรฐานไปยังไฟล์หรือแก้ไขข้างต้นอย่างชัดเจนเพื่อสร้างไฟล์เอาต์พุตที่ต้องการ

Caveats:การขยายไฟล์เทมเพลตจะไม่ทำงานหากไฟล์มีเครื่องหมายคำพูดคู่ที่ไม่ใช้ค่า Escape ( ") เพื่อเหตุผลด้านความปลอดภัยเราอาจรวมถึงการตรวจสอบสติที่ชัดเจนหรือดีกว่าดำเนินการเปลี่ยนแปลงการหลบหนีของเชลล์หากไฟล์เทมเพลตถูกสร้างโดยนิติบุคคลภายนอก


1
เป็นเรื่องดีที่จะเห็นผู้คนใช้งานเชลล์สคริปต์เพื่อทำสิ่งที่สนุกเช่นนี้ เพื่อนของฉันเขียนนาฬิกาในเชลล์สคริปต์ github.com/tablespoon/fun/blob/master/cli-clock สนุกครั้งแน่นอน
ลูกไก่

2
เมื่อฉันพบคำตอบนี้เมื่อต้องการทำสิ่งนี้ด้วยตัวเองฉันได้ใช้วิธีการที่ไร้เดียงสาและโพสต์ไว้ใน Github: github.com/nealian/bash-unsafe-templates
Pyrocater

9

วิธีที่ง่ายที่สุดในการทำเช่นนี้ใน Linux CLI คือการใช้งานenvsubstและตัวแปรสภาพแวดล้อม

ไฟล์เทมเพลตตัวอย่างapache.tmpl:

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

เรียกใช้envsubstและส่งออกผลลัพธ์ไปยังไฟล์ใหม่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

เอาท์พุท:

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

2
มันเป็นส่วนหนึ่งของ GNU gettext ดังนั้นจึงมีอยู่ในแพลตฟอร์ม Unix ที่สำคัญทั้งหมด (รวมถึง macOS X) เช่นเดียวกับ Microsoft Windows
tricasse

4

คุณอาจจะควรจะมองเข้าไปในระบบการจัดการการกำหนดค่าเช่นหุ่นกระบอกหรือเชฟ เหล่านี้สามารถทำในสิ่งที่คุณอธิบายข้างต้นและมากมากขึ้น


1
ขอบคุณ ฉันได้ติดตั้งและใช้งานเชฟอย่างแน่นอน แต่มันเพิ่มค่าใช้จ่ายจำนวนมากเมื่อคุณต้องเขียนตำราของคุณเอง ฉันไม่รู้ภาษาการเขียนโปรแกรมทับทิมและข้อสรุปของฉันคือ มันง่ายกว่าที่จะทำกับเชลล์สคริปสำหรับกรณีที่ง่ายกว่า
Markus

ดูเหมือนว่าPuppetและChefต่างก็ใช้ ERB เป็นแม่แบบและนั่นก็เป็นเรื่องง่ายที่จะเริ่มต้นด้วย รับตัวแปรnameสตริง<%= name %>ในเทมเพลตจะถูกแทนที่ด้วยnameค่าของ วิธีที่คุณกำหนดnameภายนอกเทมเพลตนั้นแตกต่างกันระหว่างสองระบบอย่างชัดเจน
Mike Renfro

1
ใช่การสร้างเทมเพลต (พร้อมพ่อครัว) นั้นง่ายมาก แต่การใช้พ่อครัวเป็น Framework (และการเขียนตำรา) ต้องใช้เวลามาก ในการรับข้อมูลลงในเทมเพลตคุณต้องเข้าใจว่าที่ไหนและอย่างไรที่เชฟจัดการ "การผสาน" แหล่งข้อมูลและสิ่งอื่น ๆ มากมาย ฉันเริ่มเขียนตำราของตัวเองแล้ว แต่เชลล์สคริปท์ในกรณีพิเศษของฉันจะเร็วขึ้น 100 เท่า ...
Markus

การได้รับโครงสร้างพื้นฐานสำหรับพ่อครัวหรือหุ่นเชิดอาจเป็นเรื่องที่เจ็บปวดหรือคุณอาจลองคิดดูว่าจะทำอย่างไรให้หัวขาดซึ่งเป็นการผจญภัยที่สนุกสนาน Ansible ทำงานได้ดีในการดึงหรือดันโหมดออกจากกล่องดังนั้นมันอาจทำให้เกิดความสมดุลที่ดีขึ้นในการหามันและเขียนสคริปต์ด้วยตัวคุณเอง docs.ansible.com/template_module.html
chicks

4

หากคุณต้องการมีน้ำหนักเบาและแม่ที่แท้จริงมากกว่ารหัสเปลือกที่สร้างไฟล์ใหม่ทางเลือกปกติsedและหรือawk perlนี่คือลิงค์เดียว: http://savvyadmin.com/generate-text-from-templates-scripts-and-csv-data/

ฉันฉันจะใช้ภาษาจริงเช่น perl, tcl, python, ruby ​​หรืออย่างอื่นในชั้นเรียนนั้น สิ่งที่สร้างขึ้นสำหรับการเขียนสคริปต์ ทุกคนมีเครื่องมือสร้างเทมเพลตที่เรียบง่ายและมีตัวอย่างมากมายใน Google


4

ฉันใช้shtplสำหรับสิ่งนั้น (โครงการส่วนตัวของฉันซึ่งหมายความว่ามันไม่ได้ใช้กันอย่างแพร่หลาย แต่บางทีคุณอาจต้องการทดสอบต่อไป)

ตัวอย่างเช่นคุณต้องการสร้าง / etc / network / interfaces จาก csv-file คุณสามารถทำได้ดังนี้:

เนื้อหาไฟล์ CSV (ที่นี่ 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

เทมเพลต (ที่นี่ interfaces.tpl):

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

#% done < "$CSVFILE"

คำสั่ง:

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

ผล:

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

สนุก!


1

ฉันปรับปรุงคำตอบของ FooF เพื่อให้ผู้ใช้ไม่จำเป็นต้องใช้เครื่องหมายอัญประกาศคู่ unescape ด้วยตนเอง:

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

1

ฉันเพิ่งเผยแพร่สคริปต์ทุบตีที่ประสบความสำเร็จเพียงแค่นั้นโดยใช้ไวยากรณ์แม่แบบเหมือนจินจา มันเรียกว่าคุกกี้ นี่คือตัวอย่าง:

การสาธิตคุกกี้


คำถามนี้จะตอบคำถามได้อย่างไร
RalfFriedl

1
@RalfFriedl ฉันไม่แน่ใจว่าคุณหมายถึงอะไร ฉันเพิ่งอ่านคำถามที่คิดว่าฉันต้องพลาดบางสิ่งบางอย่าง แต่ฉันไม่เห็นมัน ในความเป็นจริงวิธีนี้จะไม่ได้คำตอบใด ๆ ที่เป็นส่วนหนึ่งของคำถามที่ถูกถาม? มันเกือบจะเหมือนกับว่าฉันได้สร้างคุกกี้โดยมีจุดประสงค์เพียงอย่างเดียวในการตอบคำถามนี้ ... ซึ่งฉันก็ทำแบบนั้นถึงแม้ว่าฉันจะพูดออกมาในหัวของฉันในเวลานั้น :)
Bryan Bugyi

ฉันสายไปนิดหน่อยที่จะเล่นเป็นคนกลาง แต่บางทีราล์ฟก็เป็นคนกระตุ้นให้คุณอธิบายว่าคุกกี้ตอบคำถามได้อย่างไรแทนที่จะพยายามที่จะอธิบายให้คุณรู้ว่าโครงการของตัวเองดีพอที่จะตัดสินว่ามันจะแก้ปัญหาของใครบางคน .
abathur

0

ฉันอาจไปงานเลี้ยงสายนี้ อย่างไรก็ตามฉันพบปัญหาเดียวกันมากและฉันเลือกที่จะสร้างเครื่องมือเท็มเพลต BASH ของฉันเองในโค้ดสองสามบรรทัด:

ให้บอกว่าคุณมีสิ่งนี้file.template:

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

และrulesไฟล์นี้:

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

คุณรันคำสั่งนี้:

templater rules < file.template

คุณได้รับสิ่งนี้:

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

คุณสามารถติดตั้งได้โดย:

 bpkg install vicentebolea/bash-templater

นี่คือเว็บไซต์โครงการ


0

ในการขยายคำตอบที่ยอดเยี่ยมของ @FooF (บรรทัดใหม่ไม่ได้จัดรูปแบบในความคิดเห็น) โดยใช้ตัวละครที่ควบคุม heredoc + คุณสามารถอนุญาตตัวละครและชื่อไฟล์โดยพลการ:

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

สิ่งนี้ยอมรับอักขระที่ไม่เป็นโมฆะและจะตัดทอนก่อนเวลาหาก^Dพบ3 ไบต์ในบรรทัดของตนเอง (ไม่เคยแนบเนียน) zsh ยังรองรับเทอร์มิเนเตอร์ที่เป็นโมฆะดังนั้นprintf '\x00\x00\x00'จะใช้งานได้ templateแม้ทำงานกับชื่อไฟล์ทุจริตเช่น:

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

ระวังแม่เปลือกสามารถ "ขยาย" $(launch_nukes.sh --target $(curl -sL https://freegeoip.app/csv/ | cut -d, -f 9,10))คำสั่งโดยพลการเช่น ด้วยพลังอันยิ่งใหญ่ ...

แก้ไข: หากคุณไม่ต้องการให้ไฟล์ของคุณเปิดนิวเคลียร์ที่คุณเพียงแค่sudo -u nobody sh(หรือผู้ใช้ที่ปลอดภัยคนอื่น) ก่อน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.