Câu trả lời:
Đã cho template.txt:
Số này là $ {i} Từ này là $ {word}
chúng ta chỉ cần nói:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
Cảm ơn Jonathan Leffler về mẹo để truyền nhiều -e
đối số cho cùng một sed
lời mời.
cat
. Tất cả bạn cần là sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
.
sed
sẽ mong đợi một văn bản thoát, đó là một rắc rối.
Đây là một giải pháp từ yottatsa cho một câu hỏi tương tự chỉ thay thế cho các biến như $ VAR hoặc $ {VAR}, và là một câu hỏi ngắn gọn
i=32 word=foo envsubst < template.txt
Tất nhiên nếu tôi và từ trong môi trường của bạn, thì đó chỉ là
envsubst < template.txt
Trên máy Mac của tôi, có vẻ như nó đã được cài đặt như một phần của gettext và từ MacGPG2
Đây là một cải tiến cho giải pháp từ mogsie cho một câu hỏi tương tự, giải pháp của tôi không yêu cầu bạn phải bỏ qua các trích dẫn kép, mogsie cũng vậy, nhưng anh ấy là một lót!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
Sức mạnh của hai giải pháp này là bạn chỉ nhận được một vài loại mở rộng vỏ không xảy ra bình thường $ ((...)), `...` và $ (...), mặc dù dấu gạch chéo ngược là một thoát ký tự ở đây, nhưng bạn không phải lo lắng rằng việc phân tích cú pháp có lỗi và nó thực hiện nhiều dòng tốt.
envsubst
không hoạt động nếu envars của bạn không xuất khẩu.
envsubst
, như tên gọi của nó, chỉ nhận ra các biến môi trường , không phải biến vỏ . Cũng đáng lưu ý rằng đó envsubst
là một tiện ích GNU và do đó không được cài đặt sẵn hoặc có sẵn trên tất cả các nền tảng.
Sử dụng /bin/sh
. Tạo một tập lệnh shell nhỏ đặt các biến và sau đó phân tích mẫu bằng cách sử dụng chính shell. Giống như vậy (chỉnh sửa để xử lý các dòng mới chính xác):
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
lệnh trong đầu vào sẽ được thực hiện. Nếu mẫu là: "các từ là; rm -rf $ HOME" bạn sẽ mất các tệp.
read
lệnh, như được viết, cắt khoảng trắng dẫn đầu và dấu cách từ mỗi dòng và \
ký tự 'ăn' ., (C) chỉ sử dụng điều này nếu bạn hoàn toàn tin tưởng hoặc kiểm soát đầu vào, bởi vì các thay thế lệnh ( `…`
hoặc $(…)
) được nhúng trong đầu vào cho phép thực thi các lệnh tùy ý do sử dụng eval
. Cuối cùng, có một cơ hội nhỏ echo
sai lầm khi bắt đầu một dòng cho một trong các tùy chọn dòng lệnh của nó.
Tôi đã suy nghĩ về điều này một lần nữa, với sự quan tâm gần đây và tôi nghĩ rằng công cụ mà tôi nghĩ ban đầu là m4
, bộ xử lý macro cho autotools. Vì vậy, thay vì biến tôi đã chỉ định ban đầu, bạn sẽ sử dụng:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
cho việc sử dụng thay thế / tạo khuôn mẫu đơn giản này, như đã đề cập trong các câu trả lời khác. m4
là một công cụ tuyệt vời, nhưng nó là một bộ tiền xử lý đầy đủ với nhiều tính năng hơn và do đó phức tạp có thể không cần thiết nếu bạn chỉ muốn thay thế một số biến.
template.txt
Variable 1 value: ${var1}
Variable 2 value: ${var2}
dữ liệu
#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"
trình phân tích cú pháp
#!/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
`…`
hoặc $(…)
) được nhúng trong đầu vào cho phép thực thi các lệnh tùy ý do sử dụng eval
và thực thi trực tiếp mã shell do sử dụng source
. Ngoài ra, dấu ngoặc kép trong đầu vào bị loại bỏ lặng lẽ và echo
có thể nhầm phần đầu của một dòng cho một trong các tùy chọn dòng lệnh của nó.
Đây là một chức năng Bash mạnh mẽ - mặc dù sử dụng eval
- nên an toàn khi sử dụng.
Tất cả ${varName}
các tham chiếu biến trong văn bản đầu vào được mở rộng dựa trên các biến của vỏ gọi.
Không có gì khác được mở rộng: không tham chiếu biến có tên không được đặt trong {...}
(chẳng hạn như $varName
), cũng không phải thay thế lệnh ( $(...)
và cú pháp kế thừa `...`
), cũng không phải thay thế số học ( $((...))
và cú pháp kế thừa $[...]
).
Để coi một $
nghĩa đen, - hãy \
xem nó; ví dụ:\${HOME}
Lưu ý rằng đầu vào chỉ được chấp nhận thông qua stdin .
Thí dụ:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
Mã nguồn chức năng:
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
}
Chức năng giả định rằng không có 0x1
, 0x2
, 0x3
, và 0x4
ký tự điều khiển có mặt trong đầu vào, bởi vì những ký tự. được sử dụng nội bộ - vì hàm xử lý văn bản , đó phải là một giả định an toàn.
eval
nó là khá an toàn để sử dụng.
"
đúng cách!)
${FOO:-bar}
hoặc chỉ xuất ra thứ gì đó nếu nó được đặt - ${HOME+Home is ${HOME}}
. Tôi nghi ngờ với một phần mở rộng nhỏ, nó cũng có thể trả về mã thoát cho các biến bị thiếu ${FOO?Foo is missing}
nhưng hiện tại tldp.org/LDP/abs/html/parameter-substlation.html có một danh sách các biến này nếu điều đó giúp
Tạo rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
Và template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
Kết xuất mẫu:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
, bạn đang chạy mã đó.
eval "echo \"$(cat $1)\""
Hoạt động tuyệt vời!
Đây là giải pháp của tôi với perl dựa trên câu trả lời trước, thay thế các biến môi trường:
perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1}?$ENV{$1}:"missing variable $1")/eg' < infile > outfile
Nếu bạn cởi mở để sử dụng Perl , đó sẽ là gợi ý của tôi. Mặc dù có thể có một số chuyên gia sed và / hoặc AWK có thể biết cách làm điều này dễ dàng hơn nhiều. Nếu bạn có một ánh xạ phức tạp hơn không chỉ là dbName cho các thay thế của mình, bạn có thể mở rộng điều này khá dễ dàng, nhưng bạn cũng có thể đưa nó vào một tập lệnh Perl tiêu chuẩn vào thời điểm đó.
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
Một tập lệnh Perl ngắn để làm một cái gì đó phức tạp hơn một chút (xử lý nhiều khóa):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
Nếu bạn đặt tên cho tập lệnh trên là tập lệnh thay thế, thì nó có thể được sử dụng như sau:
replace-script < yourfile | mysql
Đây là một cách để có được trình bao thay thế cho bạn, như thể nội dung của tệp được nhập giữa các dấu ngoặc kép.
Sử dụng ví dụ về template.txt với nội dung:
The number is ${i}
The word is ${word}
Dòng sau sẽ khiến shell nội suy nội dung của template.txt và ghi kết quả vào tiêu chuẩn.
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
Giải trình:
i
và word
được thông qua khi các biến môi trường được chuyển sang thực thi sh
.sh
thực hiện các nội dung của chuỗi nó được thông qua.echo "
' + " $(cat template.txt)
" + ' "
'"
, " $(cat template.txt)
" trở thành đầu ra của cat template.txt
.sh -c
trở thành:
echo "The number is ${i}\nThe word is ${word}"
,i
và word
là các biến môi trường được chỉ định.'$(rm -rf ~)'$(rm -rf ~)
các trích dẫn bằng chữ trong tệp mẫu sẽ khớp với các mẫu bạn đã thêm trước khi mở rộng.
'$(echo a)'$(echo a)
. Nó sản xuất 'a'a
. Điều chính đang xảy ra là đầu tiên echo a
bên trong '
đang được đánh giá, có thể không phải là những gì bạn mong đợi kể từ khi nó xảy ra '
, nhưng là hành vi tương tự như bao gồm '
trong một "
chuỗi trích dẫn.
"
chuỗi trích dẫn (bao gồm $(...)
) là điểm.
${varname}
, chứ không phải mở rộng rủi ro bảo mật cao hơn.
echo "
, theo sau là một chuỗi trích dẫn kép với các ký tự bằng chữ template.txt
, theo sau là một chuỗi ký tự khác "
, tất cả được nối vào một đối số duy nhất được truyền vào sh -c
. Bạn đúng rằng '
không thể khớp được (vì nó được tiêu thụ bởi lớp vỏ bên ngoài thay vì được chuyển sang lớp bên trong), nhưng "
chắc chắn là có thể, do đó, một mẫu chứa Gotcha"; rm -rf ~; echo "
có thể được thực thi.
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
làm read
lệnh, nếu không, bạn sẽ loại bỏ khoảng trắng hàng đầu và dấu kiểm từ mỗi dòng đầu vào. Ngoài ra, echo
có thể nhầm phần đầu của một dòng với một trong các tùy chọn dòng lệnh của nó, vì vậy tốt hơn nên sử dụng printf '%s\n'
. Cuối cùng, an toàn hơn để trích dẫn kép ${1}
.
Tôi sẽ đề nghị sử dụng một cái gì đó như Sigil : https://github.com/gliderlabs/sigil
Nó được biên dịch thành một nhị phân duy nhất, vì vậy nó rất dễ cài đặt trên các hệ thống.
Sau đó, bạn có thể thực hiện một lớp lót đơn giản như sau:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
Điều này an toàn hơn nhiều eval
và dễ dàng hơn khi sử dụng regex hoặcsed
cat
và sử dụng <my-file.conf.template
thay vào đó để bạn đưa ra sigil
một tệp xử lý thực sự thay vì một bộ xếp hình.
Tôi tìm thấy chủ đề này trong khi tự hỏi điều tương tự. Nó truyền cảm hứng cho tôi về điều này (cẩn thận với backticks)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
là$(< file)
eval echo "\"$(cat FILE)\""
nhưng điều đó vẫn có thể bị thiếu trong các trích dẫn kép trong đầu vào bị loại bỏ.
`…`
hoặc $(…)
) được nhúng trong đầu vào cho phép thực thi các lệnh tùy ý do sử dụng eval
.
Có rất nhiều sự lựa chọn ở đây, nhưng tôi nghĩ rằng tôi sẽ ném tôi vào đống. Nó dựa trên perl, chỉ nhắm mục tiêu các biến có dạng $ {...}, lấy tệp để xử lý làm đối số và xuất ra tệp được chuyển đổi trên thiết bị xuất chuẩn:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
Tất nhiên tôi không thực sự là một người perl, vì vậy có thể dễ dàng có một lỗ hổng chết người (mặc dù làm việc cho tôi).
Env::import();
dòng - nhập khẩu được ngụ ý bởi use
. Ngoài ra, tôi khuyên bạn không nên xây dựng toàn bộ đầu ra trong bộ nhớ trước: chỉ cần sử dụng print;
thay vì $text .= $_;
bên trong vòng lặp và thả lệnh sau vòng lặp print
.
Nó có thể được thực hiện trong bash chính nó nếu bạn có quyền kiểm soát định dạng tệp cấu hình. Bạn chỉ cần nguồn (".") Tệp cấu hình chứ không phải chia nhỏ nó. Điều đó đảm bảo các biến được tạo trong ngữ cảnh của shell hiện tại (và tiếp tục tồn tại) chứ không phải là lớp con (nơi biến biến mất khi lớp vỏ con thoát ra).
$ 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
Nếu tệp cấu hình của bạn không thể là tập lệnh shell, bạn chỉ có thể 'biên dịch' nó trước khi thực hiện (quá trình biên dịch phụ thuộc vào định dạng đầu vào của bạn).
$ 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
Trong trường hợp cụ thể của bạn, bạn có thể sử dụng một cái gì đó như:
$ 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
Sau đó chuyển đầu ra của go.bash vào MySQL và voila, hy vọng bạn sẽ không phá hủy cơ sở dữ liệu của mình :-).
go.bash
), bạn đã hiểu nhầm về kết quả - chúng không phải là một phần của giải pháp, chúng chỉ là một cách thể hiện rằng các biến đang đặt chính xác.