ฉันต้องการไพพ์เอาท์พุทของไฟล์ "เทมเพลต" ลงใน MySQL ไฟล์ที่มีตัวแปรเช่น${dbName}
กระจาย อรรถประโยชน์บรรทัดคำสั่งเพื่อแทนที่อินสแตนซ์เหล่านี้คืออะไรและดัมพ์เอาต์พุตไปยังเอาต์พุตมาตรฐาน?
ฉันต้องการไพพ์เอาท์พุทของไฟล์ "เทมเพลต" ลงใน MySQL ไฟล์ที่มีตัวแปรเช่น${dbName}
กระจาย อรรถประโยชน์บรรทัดคำสั่งเพื่อแทนที่อินสแตนซ์เหล่านี้คืออะไรและดัมพ์เอาต์พุตไปยังเอาต์พุตมาตรฐาน?
คำตอบ:
ใจเย็นๆ !
ให้ template.txt:
ตัวเลขคือ $ {i} คำนี้คือ $ {word}
เราต้องพูดว่า:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
ขอบคุณ Jonathan Leffler สำหรับเคล็ดลับในการส่ง-e
อาร์กิวเมนต์จำนวนมากไปยังsed
คำร้องขอเดียวกัน
cat
นอกจากนี้คุณยังไม่จำเป็นต้องใช้ sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
ทั้งหมดที่คุณต้องการก็คือ
sed
คาดว่าจะมีข้อความหลบหนีซึ่งเป็นเรื่องยุ่งยาก
นี่คือวิธีการแก้ปัญหาจาก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
อำนาจในการแก้ปัญหาทั้งสองนี้คือคุณจะได้รับการขยายเชลล์ไม่กี่ประเภทที่ไม่ได้เกิดขึ้นตามปกติ $ ((... )), `... ', และ $ (... ) แม้ว่าแบ็กสแลชจะเป็น หลบหนีตัวละครที่นี่ แต่คุณไม่ต้องกังวลว่าการแยกวิเคราะห์มีข้อบกพร่องและมันก็ทำได้หลายบรรทัด
envsubst
ไม่ทำงานหากไม่ได้ส่งออก envars ของคุณ
envsubst
ตามที่ชื่อแนะนำให้รับรู้เฉพาะตัวแปรสภาพแวดล้อมไม่ใช่ตัวแปรเชลล์ นอกจากนี้ยังเป็นที่น่าสังเกตว่าenvsubst
เป็นโปรแกรมอรรถประโยชน์ของGNUดังนั้นจึงไม่ได้ติดตั้งไว้ล่วงหน้าหรือพร้อมใช้งานบนแพลตฟอร์มทั้งหมด
/bin/sh
ใช้ สร้างเชลล์สคริปต์ขนาดเล็กที่ตั้งค่าตัวแปรแล้ววิเคราะห์แม่แบบโดยใช้เชลล์เอง ชอบ (แก้ไขเพื่อจัดการบรรทัดใหม่อย่างถูกต้อง):
the number is ${i}
the word is ${word}
#!/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
bash
คำสั่งทั้งหมดในอินพุตจะถูกดำเนินการ หากเทมเพลตคือ: "the words is; rm -rf $ HOME" คุณจะหลวมไฟล์
read
คำสั่งตามที่เขียนไว้จะจดจ้องช่องว่างที่นำหน้าและต่อท้ายจากแต่ละบรรทัดและ 'eats' \
chars. (c) ใช้สิ่งนี้เฉพาะเมื่อคุณเต็มที่ ความไว้วางใจหรือควบคุมการป้อนข้อมูลเพราะแทนคำสั่ง ( `…`
หรือ$(…)
) eval
ที่ฝังอยู่ในการป้อนข้อมูลที่ช่วยให้การดำเนินการของคำสั่งโดยพลเกิดจากการใช้ ในที่สุดก็มีโอกาสเล็กน้อยที่echo
เกิดความผิดพลาดในการเริ่มต้นบรรทัดสำหรับหนึ่งในตัวเลือกบรรทัดคำสั่ง
ฉันกำลังคิดเกี่ยวกับสิ่งนี้อีกครั้งให้ความสนใจเมื่อเร็ว ๆ นี้และฉันคิดว่าเครื่องมือที่ฉันคิดเดิมคือm4
ตัวประมวลผลแมโครสำหรับเครื่องมืออัตโนมัติ ดังนั้นแทนที่จะเป็นตัวแปรที่ฉันระบุไว้ตั้งแต่แรกคุณควรใช้:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
สำหรับการเปลี่ยนตัวแปร / การสร้างเทมเพลตอย่างง่ายนี้ตามที่ระบุไว้ในคำตอบอื่น ๆ m4
เป็นเครื่องมือที่ยอดเยี่ยม แต่เป็น preprocessor แบบเต็มเป่าที่มีคุณสมบัติมากมายและความซับซ้อนซึ่งอาจไม่จำเป็นหากคุณต้องการแทนที่ตัวแปรบางตัว
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
`…`
หรือ$(…)
) ที่ฝังอยู่ในการป้อนข้อมูลที่ช่วยให้การดำเนินการของคำสั่งโดยพลเกิดจากการใช้และการดำเนินการโดยตรงของรหัสเปลือกเนื่องจากการใช้eval
source
นอกจากนี้อัญประกาศคู่ในอินพุตจะถูกทิ้งอย่างเงียบ ๆ และecho
อาจผิดพลาดจุดเริ่มต้นของบรรทัดสำหรับหนึ่งในตัวเลือกบรรทัดคำสั่ง
นี่คือฟังก์ชั่นทุบตีที่แข็งแกร่งที่แม้จะใช้อยู่ก็ตาม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
ตัวควบคุมที่มีอยู่ในการป้อนข้อมูลเพราะตัวอักษรเหล่านั้น ถูกใช้ภายใน - เนื่องจากฟังก์ชั่นประมวลผลข้อความนั่นควรเป็นข้อสันนิษฐานที่ปลอดภัย
eval
แล้วมันก็ค่อนข้างปลอดภัยในการใช้
"
อย่างถูกต้อง!)
${FOO:-bar}
หรือสิ่งที่ส่งออกเฉพาะในกรณีที่มีการตั้งค่า ${HOME+Home is ${HOME}}
- ฉันสงสัยว่ามีส่วนขยายเล็ก ๆ น้อย ๆ มันอาจส่งคืนรหัสทางออกสำหรับตัวแปรที่ขาดหายไป${FOO?Foo is missing}
แต่ในปัจจุบันไม่ได้ tldp.org/LDP/abs/html/parameter-substitution.htmlมีรายการของสิ่งเหล่านี้หากช่วยได้
สร้าง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
$(rm -rf ~)
อยู่แสดงว่าคุณกำลังใช้งานโค้ดดังกล่าวอยู่
eval "echo \"$(cat $1)\""
ใช้งานได้ดี!
นี่คือทางออกของฉันด้วย perl ตามคำตอบเดิมแทนที่ตัวแปรสภาพแวดล้อม:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
หากคุณเปิดให้ใช้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
นี่เป็นวิธีที่จะทำให้เชลล์ทำการทดแทนสำหรับคุณราวกับว่าเนื้อหาของไฟล์ถูกพิมพ์ระหว่างเครื่องหมายคำพูดคู่
ใช้ตัวอย่างของ 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 ~)
เครื่องหมายคำพูดตามตัวอักษรในไฟล์เทมเพลตจะตรงกับที่คุณเพิ่มก่อนที่จะขยาย
'$(echo a)'$(echo a)
รุ่นของการทดสอบที่ไม่ได้ลบไดเรกทอรีบ้านของคุณคือ 'a'a
มันผลิต สิ่งสำคัญที่เกิดขึ้นคือสิ่งแรกที่echo a
อยู่ภายใน'
ได้รับการประเมินซึ่งอาจไม่ใช่สิ่งที่คุณคาดหวังตั้งแต่เข้ามา'
แต่เป็นพฤติกรรมเดียวกันกับที่รวม'
อยู่ใน"
สตริงที่ยกมา
"
สตริงที่อ้างถึง (รวมถึง$(...)
) เป็นจุด
${varname}
ไม่ขยายความเสี่ยงด้านความปลอดภัยที่สูงกว่า
echo "
ตามด้วยสตริงยกมาสองครั้งกับ contetns ที่แท้จริงของtemplate.txt
, ตามมาด้วยอีกสตริงตัวอักษรทั้งหมดที่ตัดแบ่งเป็นอาร์กิวเมนต์เดียวผ่านไป"
sh -c
คุณพูดถูกว่า'
ไม่สามารถจับคู่ได้ (เนื่องจากเชลล์ด้านนอกใช้แทนการส่งผ่านไปยังส่วนใน) แต่"
สามารถทำได้อย่างแน่นอนดังนั้นเทมเพลตที่มีอยู่Gotcha"; rm -rf ~; echo "
จะสามารถดำเนินการได้
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
\$(date)
while IFS= read -r line; do
เป็นread
คำสั่งไม่เช่นนั้นคุณจะต้องตัดช่องว่างนำหน้าและตามหลังช่องว่างจากแต่ละบรรทัดอินพุต นอกจากนี้ยังอาจจะเข้าใจผิดว่าจุดเริ่มต้นของสายหนึ่งในตัวเลือกบรรทัดคำสั่งของมันจึงดีกว่าที่จะใช้echo
สุดท้ายก็ปลอดภัยในการอ้างดับเบิลprintf '%s\n'
${1}
ฉันขอแนะนำให้ใช้บางอย่างเช่นSigil : https://github.com/gliderlabs/sigil
มันถูกรวบรวมเป็นไบนารี่เดียวดังนั้นมันจึงง่ายมากที่จะติดตั้งบนระบบ
จากนั้นคุณสามารถทำหนึ่งซับง่าย ๆ ดังต่อไปนี้:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
วิธีนี้ปลอดภัยกว่าeval
และง่ายกว่าจากนั้นใช้ regex หรือsed
cat
และใช้<my-file.conf.template
แทนดังนั้นคุณจึงให้sigil
การจัดการไฟล์จริงแทน FIFO
ฉันพบกระทู้นี้ในขณะที่สงสัยในสิ่งเดียวกัน มันเป็นแรงบันดาลใจให้ฉันทำอย่างนี้ (ระวังกับ backticks)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
เป็น$(< file)
eval echo "\"$(cat FILE)\""
แต่อาจจะยังสั้นอยู่ในเครื่องหมายคำพูดคู่ในอินพุตนั้น
`…`
หรือ$(…)
) eval
ที่ฝังอยู่ในการป้อนข้อมูลที่ช่วยให้การดำเนินการของคำสั่งโดยพลเกิดจากการใช้
มีตัวเลือกมากมายที่นี่ แต่ฉันคิดว่าฉันจะโยนระเบิดใส่กอง มันขึ้นอยู่กับ Perl เพียงตัวแปรเป้าหมายของรูปแบบ $ {... } ใช้ไฟล์ในการประมวลผลเป็นอาร์กิวเมนต์และส่งออกไฟล์ที่แปลงเมื่อ stdout:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
แน่นอนว่าฉันไม่ใช่คน Perl จริง ๆ ดังนั้นอาจมีข้อบกพร่องร้ายแรงได้ง่าย ๆ (เหมาะสำหรับฉัน)
Env::import();
สาย - use
การนำเข้าก็ส่อให้เห็นโดย นอกจากนี้ฉันไม่แนะนำให้สร้างเอาต์พุตทั้งหมดในหน่วยความจำก่อน: เพียงใช้print;
แทน$text .= $_;
ลูปภายในและวางprint
คำสั่งpost-loop
มันสามารถทำได้ในทุบตีตัวเองถ้าคุณมีการควบคุมรูปแบบไฟล์การกำหนดค่า คุณเพียงแค่ต้องแหล่งที่มา (".") ไฟล์การกำหนดค่ามากกว่าที่จะ 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 หวังว่าคุณจะไม่ทำลายฐานข้อมูลของคุณ :-)
go.bash
) แสดงว่าคุณมีจุดผิดที่ไม่ถูกต้อง - พวกเขาไม่ได้เป็นส่วนหนึ่งของการแก้ปัญหาพวกเขาเป็นเพียงวิธีการแสดงว่าตัวแปรกำลัง ตั้งอย่างถูกต้อง
มีการแก้ไขไฟล์หลาย ๆ ไฟล์พร้อมการสำรองข้อมูล
perl -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : ""/eg' \
-i.orig \
-p config/test/*
คุณจะต้องการบางสิ่งที่แข็งแกร่งกว่าคำแนะนำปัจจุบันเพราะในขณะที่กรณีเหล่านี้ใช้ได้กับกรณีการใช้งานที่ จำกัด (สำหรับตอนนี้) แต่จะไม่เพียงพอสำหรับสถานการณ์ที่ซับซ้อนมากขึ้น
คุณต้องมีเรนเดอร์ที่ดีกว่า คุณต้องการนักแสดงที่ดีที่สุด คุณต้องการ 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