วิธีแทนที่ $ {} ตัวยึดตำแหน่งในไฟล์ข้อความได้อย่างไร


163

ฉันต้องการไพพ์เอาท์พุทของไฟล์ "เทมเพลต" ลงใน MySQL ไฟล์ที่มีตัวแปรเช่น${dbName}กระจาย อรรถประโยชน์บรรทัดคำสั่งเพื่อแทนที่อินสแตนซ์เหล่านี้คืออะไรและดัมพ์เอาต์พุตไปยังเอาต์พุตมาตรฐาน?

คำตอบ:


192

ใจเย็นๆ !

ให้ template.txt:

ตัวเลขคือ $ {i}
คำนี้คือ $ {word}

เราต้องพูดว่า:

sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt

ขอบคุณ Jonathan Leffler สำหรับเคล็ดลับในการส่ง-eอาร์กิวเมนต์จำนวนมากไปยังsedคำร้องขอเดียวกัน


13
คุณสามารถรวมคำสั่ง sed สองคำเหล่านี้เป็นคำสั่งเดียว: sed -e "s / \ $ {i} / 1 /" -e "s / \ $ {word} / dog /"; ที่มีประสิทธิภาพมากขึ้น คุณสามารถพบปัญหากับบางรุ่นของ sed ที่อาจดำเนินการดังกล่าว 100 (ปัญหาจากปีที่แล้ว - อาจยังไม่เป็นจริง แต่ระวัง HP-UX)
Jonathan Leffler

1
ขอบคุณโจนาธานสิ่งที่ฉันกำลังมองหา
Dana the Sane

3
คำใบ้เล็กน้อย: หาก "1" หรือ "dog" ในตัวอย่างที่กำหนดจะมีสัญลักษณ์ดอลลาร์คุณจะต้องหลบหนีด้วยแบ็กสแลช (มิเช่นนั้นการเปลี่ยนจะไม่เกิดขึ้น)
MatthieuP

9
catนอกจากนี้คุณยังไม่จำเป็นต้องใช้ sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.textทั้งหมดที่คุณต้องการก็คือ
HardlyKnowEm

3
จะทำอย่างไรถ้าข้อความการแทนที่เป็นรหัสผ่าน ในกรณีนี้sedคาดว่าจะมีข้อความหลบหนีซึ่งเป็นเรื่องยุ่งยาก
jpbochi

177

ปรับปรุง

นี่คือวิธีการแก้ปัญหาจากyottatsaในคำถามที่คล้ายกันที่จะแทนที่เฉพาะตัวแปรเช่น $ VAR หรือ $ {VAR} และเป็นหนึ่งซับสั้น ๆ

i=32 word=foo envsubst < template.txt

แน่นอนถ้าฉันและคำอยู่ในสภาพแวดล้อมของคุณแล้วมันเป็นเพียง

envsubst < template.txt

บน Mac ของฉันดูเหมือนว่าจะถูกติดตั้งเป็นส่วนหนึ่งของgettextและจากMacGPG2

คำตอบเก่า

นี่คือการปรับปรุงการแก้ปัญหาจากmogsieในคำถามที่คล้ายกันวิธีการแก้ปัญหาของฉันไม่ต้องการให้คุณหนีคำพูดคู่, mogsie ไม่ แต่เขาเป็นหนึ่งซับ!

eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null

อำนาจในการแก้ปัญหาทั้งสองนี้คือคุณจะได้รับการขยายเชลล์ไม่กี่ประเภทที่ไม่ได้เกิดขึ้นตามปกติ $ ((... )), `... ', และ $ (... ) แม้ว่าแบ็กสแลชจะเป็น หลบหนีตัวละครที่นี่ แต่คุณไม่ต้องกังวลว่าการแยกวิเคราะห์มีข้อบกพร่องและมันก็ทำได้หลายบรรทัด


6
ฉันพบว่าภาพเปลือยenvsubstไม่ทำงานหากไม่ได้ส่งออก envars ของคุณ
Toddius Zho

4
@ToddiusZho: ไม่มีสิ่งใดที่เป็นตัวแปรสภาพแวดล้อมที่ไม่ได้ถูกส่งออก - เป็นการส่งออกที่แม่นยำซึ่งทำให้ตัวแปรเชลล์เป็นตัวแปรสภาพแวดล้อม envsubstตามที่ชื่อแนะนำให้รับรู้เฉพาะตัวแปรสภาพแวดล้อมไม่ใช่ตัวแปรเชลล์ นอกจากนี้ยังเป็นที่น่าสังเกตว่าenvsubstเป็นโปรแกรมอรรถประโยชน์ของGNUดังนั้นจึงไม่ได้ติดตั้งไว้ล่วงหน้าหรือพร้อมใช้งานบนแพลตฟอร์มทั้งหมด
mklement0

2
บางทีอีกวิธีที่จะบอกคือ envsubst เห็นเพียงว่าเป็นตัวแปรสภาพแวดล้อมของกระบวนการดังนั้นตัวแปรเชลล์ "ปกติ" ที่คุณอาจกำหนดไว้ก่อนหน้านี้ (ในบรรทัดแยก) จะไม่สืบทอดโดยกระบวนการลูกเว้นแต่คุณจะ "ส่งออก" ในตัวอย่างการใช้งานของ gettext ด้านบนฉันกำลังแก้ไขสภาพแวดล้อม gettext ที่สืบทอดมาโดยใช้กลไก bash โดยนำหน้าคำสั่งเหล่านั้นเป็นคำสั่งที่ฉันจะเรียกใช้
plockc

1
ฉันมีหนึ่งสตริงที่มี $ HOME ในนั้นฉันพบ $ HOME ทำงานเป็นเชลล์เริ่มต้นที่ต้องทำแทน $ HOME เป็นของฉันเอง / home / zw963 แต่ดูเหมือนว่าไม่สนับสนุนการทดแทน $ (cat / etc / hostname) ดังนั้นจึงไม่สมบูรณ์ตรงกับความต้องการของฉัน
zw963

3
ขอบคุณสำหรับ "คำตอบเก่า" เพราะไม่เพียง แต่จะช่วยให้ตัวแปร แต่ยังเป็นคำสั่งเชลล์เช่น $ (คำสั่ง ls -l)
Alek

46

/bin/shใช้ สร้างเชลล์สคริปต์ขนาดเล็กที่ตั้งค่าตัวแปรแล้ววิเคราะห์แม่แบบโดยใช้เชลล์เอง ชอบ (แก้ไขเพื่อจัดการบรรทัดใหม่อย่างถูกต้อง):

ไฟล์ template.txt:

the number is ${i}
the word is ${word}

ไฟล์ script.sh:

#!/bin/sh

#Set variables
i=1
word="dog"

#Read in template one line at the time, and replace variables (more
#natural (and efficient) way, thanks to Jonathan Leffler).
while read line
do
    eval echo "$line"
done < "./template.txt"

เอาท์พุท:

#sh script.sh
the number is 1
the word is dog

2
ทำไมไม่เพียงแค่อ่านบรรทัด ทำเสียงก้อง eval "$ line"; เสร็จแล้ว <./template.txt ??? ไม่จำเป็นต้องอ่านไฟล์ทั้งหมดในหน่วยความจำเพียงเพื่อคายมันออกทีละบรรทัดผ่านการใช้หัวและส่วนท้ายอย่างเข้มข้น แต่ 'eval' ก็โอเค - ยกเว้นว่าเทมเพลตมีอักขระเชลล์เช่นเครื่องหมายคำพูดด้านหลัง
Jonathan Leffler

16
มันอันตรายมาก! bashคำสั่งทั้งหมดในอินพุตจะถูกดำเนินการ หากเทมเพลตคือ: "the words is; rm -rf $ HOME" คุณจะหลวมไฟล์
rzymek

1
@rzymek - จำไว้ว่าเขาต้องการไพพ์ไฟล์นี้โดยตรงกับฐานข้อมูล ดังนั้นดูเหมือนว่าอินพุตนั้นเชื่อถือได้
gnud

4
@gnud มีความแตกต่างระหว่างการเชื่อถือไฟล์เพียงพอที่จะจัดเก็บเนื้อหาและเชื่อถือได้เพียงพอที่จะดำเนินการสิ่งที่มีอยู่
ทำเครื่องหมาย

3
ในการจดบันทึกข้อ จำกัด : (a) เครื่องหมายอัญประกาศคู่ในอินพุตจะถูกทิ้งอย่างเงียบ ๆ (b) readคำสั่งตามที่เขียนไว้จะจดจ้องช่องว่างที่นำหน้าและต่อท้ายจากแต่ละบรรทัดและ 'eats' \ chars. (c) ใช้สิ่งนี้เฉพาะเมื่อคุณเต็มที่ ความไว้วางใจหรือควบคุมการป้อนข้อมูลเพราะแทนคำสั่ง ( `…` หรือ$(…)) evalที่ฝังอยู่ในการป้อนข้อมูลที่ช่วยให้การดำเนินการของคำสั่งโดยพลเกิดจากการใช้ ในที่สุดก็มีโอกาสเล็กน้อยที่echoเกิดความผิดพลาดในการเริ่มต้นบรรทัดสำหรับหนึ่งในตัวเลือกบรรทัดคำสั่ง
mklement0

23

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

$echo 'I am a DBNAME' | m4 -DDBNAME="database name"

1
โซลูชันนี้มีข้อบกพร่องน้อยที่สุดของคำตอบที่นี่ คุณรู้วิธีใดในการแทนที่ $ {DBNAME} แทนที่จะเป็น DBNAME เท่านั้น
Jack Davidson

@ JackDavidson ฉันจะใช้envsubstสำหรับการเปลี่ยนตัวแปร / การสร้างเทมเพลตอย่างง่ายนี้ตามที่ระบุไว้ในคำตอบอื่น ๆ m4เป็นเครื่องมือที่ยอดเยี่ยม แต่เป็น preprocessor แบบเต็มเป่าที่มีคุณสมบัติมากมายและความซับซ้อนซึ่งอาจไม่จำเป็นหากคุณต้องการแทนที่ตัวแปรบางตัว
imiric

13

template.txt

Variable 1 value: ${var1}
Variable 2 value: ${var2}

data.sh

#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"

parser.sh

#!/usr/bin/env bash

# args
declare file_data=$1
declare file_input=$2
declare file_output=$3

source $file_data
eval "echo \"$(< $file_input)\"" > $file_output

./parser.sh data.sh template.txt parsed_file.txt

parsed_file.txt

Variable 1 value: value 1
Variable 2 value: value 2

1
ที่ได้รับการตั้งข้อสังเกตอื่น ๆ : เพียงใช้นี้ถ้าคุณอย่างเต็มที่ความไว้วางใจหรือควบคุมการป้อนข้อมูลเพราะแทนคำสั่ง ( `…` หรือ$(…)) ที่ฝังอยู่ในการป้อนข้อมูลที่ช่วยให้การดำเนินการของคำสั่งโดยพลเกิดจากการใช้และการดำเนินการโดยตรงของรหัสเปลือกเนื่องจากการใช้eval sourceนอกจากนี้อัญประกาศคู่ในอินพุตจะถูกทิ้งอย่างเงียบ ๆ และechoอาจผิดพลาดจุดเริ่มต้นของบรรทัดสำหรับหนึ่งในตัวเลือกบรรทัดคำสั่ง
mklement0

โชคไม่ดีที่นี่จะตัดเครื่องหมายอัญประกาศคู่ (") ทั้งหมดออกจากไฟล์ผลลัพธ์มีวิธีการเดียวกันโดยไม่ลบเครื่องหมายคำพูดคู่ออกหรือไม่
Ivaylo Slavov

ฉันพบสิ่งที่ฉันกำลังมองหาที่นี่: stackoverflow.com/a/11050943/795158 ; ฉันใช้ envsubst ข้อแตกต่างคือต้องส่งออก vars ซึ่งใช้ได้กับฉัน
Ivaylo Slavov

ถ้าไฟล์ข้อความมี "` "หรือ" " substitude จะล้มเหลว
shuiqiang

12

นี่คือฟังก์ชั่นทุบตีที่แข็งแกร่งที่แม้จะใช้อยู่ก็ตามevalควรจะปลอดภัยในการใช้งาน

${varName}การอ้างอิงตัวแปรทั้งหมดในข้อความอินพุตจะถูกขยายตามตัวแปรของเชลล์การเรียก

ไม่มีสิ่งใดที่ถูกขยาย: ไม่มีการอ้างอิงตัวแปรที่มีชื่อไม่ได้อยู่ใน{...}(เช่น$varName), หรือการแทนที่คำสั่ง ( $(...)และไวยากรณ์ดั้งเดิม`...`), หรือการแทนที่ทางคณิตศาสตร์ ( $((...))และไวยากรณ์ดั้งเดิม$[...])

ในการปฏิบัติต่อ a -scape อย่าง$แท้จริง \เช่น:\${HOME}

โปรดทราบว่าอินพุตได้รับการยอมรับผ่านstdinเท่านั้น

ตัวอย่าง:

$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)

รหัสที่มาฟังก์ชั่น:

expandVarsStrict(){
  local line lineEscaped
  while IFS= read -r line || [[ -n $line ]]; do  # the `||` clause ensures that the last line is read even if it doesn't end with \n
    # Escape ALL chars. that could trigger an expansion..
    IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '\1\2\3\4')
    # ... then selectively reenable ${ references
    lineEscaped=${lineEscaped//$'\4'{/\${}
    # Finally, escape embedded double quotes to preserve them.
    lineEscaped=${lineEscaped//\"/\\\"}
    eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$'
  done
}

ฟังก์ชั่นบนสมมติฐานที่ว่าไม่มี0x1, 0x2, 0x3และ0x4ตัวควบคุมที่มีอยู่ในการป้อนข้อมูลเพราะตัวอักษรเหล่านั้น ถูกใช้ภายใน - เนื่องจากฟังก์ชั่นประมวลผลข้อความนั่นควรเป็นข้อสันนิษฐานที่ปลอดภัย


2
นี่คือหนึ่งในคำตอบที่ดีที่สุดที่นี่ แม้จะใช้evalแล้วมันก็ค่อนข้างปลอดภัยในการใช้
anubhava

1
วิธีนี้ใช้ได้กับไฟล์ JSON! (หลบหนี"อย่างถูกต้อง!)
WBAR

2
สิ่งที่ดีกับการแก้ปัญหานี้คือมันจะช่วยให้คุณสามารถให้ค่าเริ่มต้นสำหรับตัวแปรที่ขาดหายไป${FOO:-bar}หรือสิ่งที่ส่งออกเฉพาะในกรณีที่มีการตั้งค่า ${HOME+Home is ${HOME}}- ฉันสงสัยว่ามีส่วนขยายเล็ก ๆ น้อย ๆ มันอาจส่งคืนรหัสทางออกสำหรับตัวแปรที่ขาดหายไป${FOO?Foo is missing}แต่ในปัจจุบันไม่ได้ tldp.org/LDP/abs/html/parameter-substitution.htmlมีรายการของสิ่งเหล่านี้หากช่วยได้
Stuart Moore

11

สร้างrendertemplate.sh:

#!/usr/bin/env bash

eval "echo \"$(cat $1)\""

และtemplate.tmpl:

Hello, ${WORLD}
Goodbye, ${CHEESE}

แสดงผลเทมเพลต:

$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl 
Hello, Foo
Goodbye, Bar

2
สิ่งนี้จะตัดสตริงที่มี
เครื่องหมาย

พยายาม: eval "echo $ (cat $ 1)" - ไม่ต้องใส่ราคาและมันก็ใช้ได้กับฉัน
access_granted

2
จากมุมมองด้านความปลอดภัยนี่เป็นข่าวร้าย หากเทมเพลตของคุณมี$(rm -rf ~)อยู่แสดงว่าคุณกำลังใช้งานโค้ดดังกล่าวอยู่
Charles Duffy

eval "echo \"$(cat $1)\"" ใช้งานได้ดี!
dev devv

10

นี่คือทางออกของฉันด้วย perl ตามคำตอบเดิมแทนที่ตัวแปรสภาพแวดล้อม:

perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile

2
มันเยี่ยมมาก ไม่เคยมี Perl แต่เมื่อคุณทำสิ่งนี้ง่ายและตรงไปตรงมา
Aaron McMillin

5

หากคุณเปิดให้ใช้Perlนั่นจะเป็นคำแนะนำของฉัน แม้ว่าจะมีบางอย่างที่อาจจะsedและ / หรือAWKผู้เชี่ยวชาญที่อาจจะรู้วิธีการทำง่ายมาก หากคุณมีการจับคู่ที่ซับซ้อนยิ่งกว่า dbName สำหรับการแทนที่ของคุณคุณสามารถขยายสิ่งนี้ได้อย่างง่ายดาย แต่คุณอาจใส่มันลงในสคริปต์ Perl มาตรฐานในตอนนั้น

perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql

สคริปต์ Perl สั้น ๆ เพื่อทำสิ่งที่ซับซ้อนกว่าเล็กน้อย (จัดการหลายคีย์):

#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;

หากคุณตั้งชื่อสคริปต์ด้านบนเป็น replace-script คุณสามารถใช้สคริปต์ดังต่อไปนี้:

replace-script < yourfile | mysql

1
ใช้งานได้กับตัวแปรเดียว แต่ฉันจะรวม 'หรือ' สำหรับตัวแปรอื่นได้อย่างไร
Dana the Sane

2
มีหลายวิธีที่คุณสามารถทำได้ด้วย Perl ทั้งหมดขึ้นอยู่กับความซับซ้อนและ / หรือความปลอดภัยที่คุณต้องการทำ ตัวอย่างที่ซับซ้อนมากขึ้นสามารถพบได้ที่นี่: perlmonks.org/?node_id=718936
Beau Simensen

3
การใช้ Perl นั้นสะอาดกว่าการใช้เปลือก ใช้เวลาในการทำให้งานนี้มากกว่าที่จะลองใช้วิธีแก้ปัญหาเชลล์อื่น ๆ ที่กล่าวถึง
jdigital

1
เมื่อเร็ว ๆ นี้ต้องแก้ไขปัญหาที่คล้ายกัน ในตอนท้ายฉันไปกับ perl (envsubst ดูสัญญาสำหรับเล็กน้อย แต่มันยากเกินไปที่จะควบคุม)
sfitts

5

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

ใช้ตัวอย่างของ template.txt กับเนื้อหา:

The number is ${i}
The word is ${word}

บรรทัดต่อไปนี้จะทำให้เชลล์เพื่อแก้ไขเนื้อหาของ template.txt และเขียนผลลัพธ์ออกมาเป็นมาตรฐาน

i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'

คำอธิบาย:

  • iและwordจะถูกส่งผ่านเป็นตัวแปรสภาพแวดล้อม scopped shการดำเนินการของ
  • sh ดำเนินการเนื้อหาของสตริงมันจะถูกส่งผ่าน
  • สตริงที่เขียนติดกันจะกลายเป็นสตริงหนึ่งสตริงนั้นคือ:
    • ' echo "' + " $(cat template.txt)" + ' "'
  • ตั้งแต่เปลี่ยนตัวอยู่ระหว่าง"" $(cat template.txt)" cat template.txtกลายเป็นการส่งออกของ
  • ดังนั้นคำสั่งที่ดำเนินการโดยsh -cจะกลายเป็น:
    • echo "The number is ${i}\nThe word is ${word}",
    • โดยที่iและwordเป็นตัวแปรสภาพแวดล้อมที่ระบุ

จากมุมมองด้านความปลอดภัยนี่เป็นข่าวร้าย หากเทมเพลตของคุณมี'$(rm -rf ~)'$(rm -rf ~)เครื่องหมายคำพูดตามตัวอักษรในไฟล์เทมเพลตจะตรงกับที่คุณเพิ่มก่อนที่จะขยาย
Charles Duffy

ฉันไม่ได้อ้างอิงราคาในแม่แบบที่ตรงกับคำพูดออกแม่แบบฉันเชื่อว่าเปลือกมีการแก้ไขแม่แบบและสตริงใน terminal อิสระ (มีประสิทธิภาพลบคำพูด) แล้วเชื่อมต่อพวกเขา '$(echo a)'$(echo a)รุ่นของการทดสอบที่ไม่ได้ลบไดเรกทอรีบ้านของคุณคือ 'a'aมันผลิต สิ่งสำคัญที่เกิดขึ้นคือสิ่งแรกที่echo aอยู่ภายใน'ได้รับการประเมินซึ่งอาจไม่ใช่สิ่งที่คุณคาดหวังตั้งแต่เข้ามา'แต่เป็นพฤติกรรมเดียวกันกับที่รวม'อยู่ใน"สตริงที่ยกมา
Apriori

ดังนั้นสิ่งนี้จึงไม่ปลอดภัยในแง่ที่อนุญาตให้ผู้สร้างเทมเพลตสามารถเรียกใช้โค้ดได้ อย่างไรก็ตามวิธีการประเมินราคาไม่ได้ส่งผลกระทบต่อความปลอดภัยจริงๆ การขยายสิ่งใด"สตริงที่อ้างถึง (รวมถึง$(...)) เป็นจุด
Apriori

นั่นคือประเด็นหรือไม่ ฉันเห็นเฉพาะพวกเขาที่ขอ${varname}ไม่ขยายความเสี่ยงด้านความปลอดภัยที่สูงกว่า
Charles Duffy

... ที่กล่าวว่าฉันจะต้องแตกต่างกัน (อีก: ราคาในเทมเพลตและเทมเพลตสามารถจับคู่ได้) เมื่อคุณใส่อ้างเดียวในสายของคุณคุณกำลังแยกเป็นสายเดียวที่ยกมาecho "ตามด้วยสตริงยกมาสองครั้งกับ contetns ที่แท้จริงของtemplate.txt, ตามมาด้วยอีกสตริงตัวอักษรทั้งหมดที่ตัดแบ่งเป็นอาร์กิวเมนต์เดียวผ่านไป" sh -cคุณพูดถูกว่า'ไม่สามารถจับคู่ได้ (เนื่องจากเชลล์ด้านนอกใช้แทนการส่งผ่านไปยังส่วนใน) แต่"สามารถทำได้อย่างแน่นอนดังนั้นเทมเพลตที่มีอยู่Gotcha"; rm -rf ~; echo "จะสามารถดำเนินการได้
Charles Duffy

4

file.tpl:

The following bash function should only replace ${var1} syntax and ignore 
other shell special chars such as `backticks` or $var2 or "double quotes". 
If I have missed anything - let me know.

script.sh:

template(){
    # usage: template file.tpl
    while read -r line ; do
            line=${line//\"/\\\"}
            line=${line//\`/\\\`}
            line=${line//\$/\\\$}
            line=${line//\\\${/\${}
            eval "echo \"$line\""; 
    done < ${1}
}

var1="*replaced*"
var2="*not replaced*"

template file.tpl > result.txt

2
สิ่งนี้ไม่ปลอดภัยเนื่องจากจะดำเนินการแทนคำสั่งในเทมเพลตหากพวกเขามีแบ็กสแลชชั้นนำเช่น\$(date)
Peter Dolberg

1
นอกเหนือจากจุดที่ถูกต้องของ Peter: ฉันขอแนะนำให้คุณใช้while IFS= read -r line; doเป็นreadคำสั่งไม่เช่นนั้นคุณจะต้องตัดช่องว่างนำหน้าและตามหลังช่องว่างจากแต่ละบรรทัดอินพุต นอกจากนี้ยังอาจจะเข้าใจผิดว่าจุดเริ่มต้นของสายหนึ่งในตัวเลือกบรรทัดคำสั่งของมันจึงดีกว่าที่จะใช้echo สุดท้ายก็ปลอดภัยในการอ้างดับเบิลprintf '%s\n' ${1}
mklement0

4

ฉันขอแนะนำให้ใช้บางอย่างเช่นSigil : https://github.com/gliderlabs/sigil

มันถูกรวบรวมเป็นไบนารี่เดียวดังนั้นมันจึงง่ายมากที่จะติดตั้งบนระบบ

จากนั้นคุณสามารถทำหนึ่งซับง่าย ๆ ดังต่อไปนี้:

cat my-file.conf.template | sigil -p $(env) > my-file.conf

วิธีนี้ปลอดภัยกว่าevalและง่ายกว่าจากนั้นใช้ regex หรือsed


2
คำตอบที่ดี! มันเป็นระบบ templating ที่เหมาะสมและทำงานได้ง่ายกว่าคำตอบอื่น ๆ
Erfan

BTW ดีกว่าที่จะหลีกเลี่ยงcatและใช้<my-file.conf.templateแทนดังนั้นคุณจึงให้sigilการจัดการไฟล์จริงแทน FIFO
Charles Duffy

2

ฉันพบกระทู้นี้ในขณะที่สงสัยในสิ่งเดียวกัน มันเป็นแรงบันดาลใจให้ฉันทำอย่างนี้ (ระวังกับ backticks)

$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world

4
ชวเลขทุบตีเพื่อ$(cat file)เป็น$(< file)
เกล็นแจ็

3
เห็นได้ชัดว่าวิธีการนี
Arthur Corenzan

@ArthurCorenzan: อันที่จริงการแบ่งบรรทัดจะถูกแทนที่ด้วยช่องว่าง ในการแก้ไขปัญหานี้คุณจะต้องใช้งานeval echo "\"$(cat FILE)\""แต่อาจจะยังสั้นอยู่ในเครื่องหมายคำพูดคู่ในอินพุตนั้น
mklement0

ที่ได้รับการตั้งข้อสังเกตอื่น ๆ : เพียงใช้นี้ถ้าคุณไว้วางใจหรือควบคุมการป้อนข้อมูลเพราะแทนคำสั่ง ( `…` หรือ$(…)) evalที่ฝังอยู่ในการป้อนข้อมูลที่ช่วยให้การดำเนินการของคำสั่งโดยพลเกิดจากการใช้
mklement0

2

มีตัวเลือกมากมายที่นี่ แต่ฉันคิดว่าฉันจะโยนระเบิดใส่กอง มันขึ้นอยู่กับ Perl เพียงตัวแปรเป้าหมายของรูปแบบ $ {... } ใช้ไฟล์ในการประมวลผลเป็นอาร์กิวเมนต์และส่งออกไฟล์ที่แปลงเมื่อ stdout:

use Env;
Env::import();

while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }

print "$text";

แน่นอนว่าฉันไม่ใช่คน Perl จริง ๆ ดังนั้นอาจมีข้อบกพร่องร้ายแรงได้ง่าย ๆ (เหมาะสำหรับฉัน)


1
ทำงานได้ดี คุณสามารถวางEnv::import();สาย - useการนำเข้าก็ส่อให้เห็นโดย นอกจากนี้ฉันไม่แนะนำให้สร้างเอาต์พุตทั้งหมดในหน่วยความจำก่อน: เพียงใช้print;แทน$text .= $_;ลูปภายในและวางprintคำสั่งpost-loop
mklement0

1

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

$ cat config.data
    export parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA
    export parm_user=pax
    export parm_pwd=never_you_mind

$ cat go.bash
    . config.data
    echo "JDBC string is " $parm_jdbc
    echo "Username is    " $parm_user
    echo "Password is    " $parm_pwd

$ bash go.bash
    JDBC string is  jdbc:db2://box7.co.uk:5000/INSTA
    Username is     pax
    Password is     never_you_mind

หากไฟล์ปรับแต่งของคุณไม่สามารถเป็นเชลล์สคริปต์ได้คุณสามารถ 'คอมไพล์' ไฟล์ก่อนดำเนินการ (การคอมไพล์ขึ้นอยู่กับรูปแบบอินพุตของคุณ)

$ cat config.data
    parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA # JDBC URL
    parm_user=pax                              # user name
    parm_pwd=never_you_mind                    # password

$ cat go.bash
    cat config.data
        | sed 's/#.*$//'
        | sed 's/[ \t]*$//'
        | sed 's/^[ \t]*//'
        | grep -v '^$'
        | sed 's/^/export '
        >config.data-compiled
    . config.data-compiled
    echo "JDBC string is " $parm_jdbc
    echo "Username is    " $parm_user
    echo "Password is    " $parm_pwd

$ bash go.bash
    JDBC string is  jdbc:db2://box7.co.uk:5000/INSTA
    Username is     pax
    Password is     never_you_mind

ในกรณีเฉพาะของคุณคุณสามารถใช้สิ่งต่อไปนี้:

$ cat config.data
    export p_p1=val1
    export p_p2=val2
$ cat go.bash
    . ./config.data
    echo "select * from dbtable where p1 = '$p_p1' and p2 like '$p_p2%' order by p1"
$ bash go.bash
    select * from dbtable where p1 = 'val1' and p2 like 'val2%' order by p1

จากนั้นไปที่การส่งออก go.bash ลงใน MySQL และ voila หวังว่าคุณจะไม่ทำลายฐานข้อมูลของคุณ :-)


1
คุณไม่ต้องส่งออกตัวแปรจากไฟล์ config.data มันก็เพียงพอแล้วที่จะตั้งค่า ดูเหมือนว่าคุณจะไม่ได้อ่านไฟล์เทมเพลตเลย หรือบางทีไฟล์เทมเพลตได้รับการแก้ไขและมีการดำเนินงาน 'echo' ... หรือว่าฉันทำอะไรหายไป?
Jonathan Leffler

1
จุดดีในการส่งออกฉันทำตามค่าเริ่มต้นเพื่อให้พร้อมใช้งานกับ subshells และไม่ก่อให้เกิดอันตรายใด ๆ เนื่องจากจะตายเมื่อออกไป ไฟล์ 'เทมเพลต' เป็นสคริปต์ของตัวเองโดยมีข้อความ echo ไม่จำเป็นต้องแนะนำไฟล์ที่สาม - โดยทั่วไปแล้วเป็นการดำเนินการตามประเภทเมล์เมอร์
paxdiablo

1
"สคริปต์ตัวเองที่มีคำสั่ง echo" ไม่ใช่แม่แบบ: มันเป็นสคริปต์ คิดว่าการอ่าน (และการบำรุงรักษา) แตกต่างกันระหว่าง <xml type = "$ TYPE"> และ echo '<xml type = "' $ TYPE '">'
ปิแอร์ - โอลิเวียร์ Vares

1
@Pierre ไม่มีคำสั่ง echo ในสคริปต์การกำหนดค่าของพวกเขาพวกเขาเป็นเพียงการส่งออกและฉันได้แสดงวิธีที่คุณสามารถหลีกเลี่ยงได้แม้จะมีการประมวลผลล่วงหน้าจำนวนน้อยที่สุด หากคุณกำลังพูดถึงคำสั่ง echo ในสคริปต์อื่น ๆ ของฉัน (เช่นgo.bash) แสดงว่าคุณมีจุดผิดที่ไม่ถูกต้อง - พวกเขาไม่ได้เป็นส่วนหนึ่งของการแก้ปัญหาพวกเขาเป็นเพียงวิธีการแสดงว่าตัวแปรกำลัง ตั้งอย่างถูกต้อง
paxdiablo

1
@paxdiablo: ดูเหมือนว่าคุณเพียงแค่ลืมคำถาม: << ฉันต้องการไพพ์เอาท์พุทของไฟล์ "เทมเพลต" ลงใน MySQL >> ดังนั้นการใช้เทมเพลตจึงเป็นคำถามไม่ใช่ "จุดจบของไม้ที่ผิด" การส่งออกตัวแปรและการสะท้อนพวกเขาในสคริปต์อื่นไม่ตอบคำถามเลย
Pierre-Olivier Vares

0

มีการแก้ไขไฟล์หลาย ๆ ไฟล์พร้อมการสำรองข้อมูล

  perl -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : ""/eg' \
    -i.orig \
    -p config/test/*

0

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

คุณต้องมีเรนเดอร์ที่ดีกว่า คุณต้องการนักแสดงที่ดีที่สุด คุณต้องการ The Renderest!

ให้ template.txt:

สวัสดี {{person}}!

วิ่ง:

$ person = Bob ./render template.txt

และคุณจะเห็นผลลัพธ์

สวัสดีบ๊อบ!

เขียนลงในไฟล์โดยเปลี่ยนเส้นทาง stdout ไปยังไฟล์:

$ person = Bob ./render template.txt> rendered.txt

และถ้าคุณมีการเรนเดอร์สคริปต์ที่มีตัวแปร $ {} ที่คุณไม่ต้องการสอดแทรก The Renderest ทำให้คุณครอบคลุมโดยที่คุณไม่ต้องทำอะไร!

ไปและรับสำเนาของคุณที่https://github.com/relaxdiego/renderest

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