Cách thay thế các biến shell trong các tệp văn bản phức tạp


84

Tôi có một số tệp văn bản trong đó tôi đã giới thiệu các biến shell (ví dụ: $ VAR1 hoặc $ VAR2).

Tôi muốn lấy các tệp đó (từng tệp một) và lưu chúng vào tệp mới, nơi tất cả các biến sẽ được thay thế.

Để thực hiện việc này, tôi đã sử dụng tập lệnh shell sau (tìm thấy trên StackOverflow):

while read line
do
    eval echo "$line" >> destination.txt
done < "source.txt"

Điều này hoạt động rất tốt trên các tệp rất cơ bản.

Nhưng trên các tệp phức tạp hơn, lệnh "eval" làm quá nhiều thứ:

  • Các dòng bắt đầu bằng "#" bị bỏ qua

  • Việc phân tích cú pháp tệp XML dẫn đến rất nhiều lỗi

Có cách nào tốt hơn để làm điều đó không? (trong shell script ... Tôi biết điều này có thể dễ dàng thực hiện với Ant chẳng hạn)

Trân trọng

Câu trả lời:


196

Nhìn vào, hóa ra trên hệ thống của tôi có một envsubstlệnh là một phần của gói gettext-base.

Vì vậy, điều này làm cho nó dễ dàng:

envsubst < "source.txt" > "destination.txt"

Lưu ý rằng nếu bạn muốn sử dụng cùng một tệp cho cả hai, bạn sẽ phải sử dụng một cái gì đó giống như moreutil's sponge, theo đề xuất của Johnny Utahh : envsubst < "source.txt" | sponge "source.txt". (Bởi vì nếu không chuyển hướng shell sẽ làm trống tệp trước khi đọc.)


4
Tuyệt quá! Nhưng nó chỉ hoạt động đối với các biến môi trường. Làm cách nào để làm cho nó hoạt động với các biến được khai báo trong tập lệnh .sh của tôi?
Ben

10
@Ben: sử dụng 'export' (trước khi gọi envsubst) cho mọi biến bạn muốn sử dụng trong envsubst
tlo

7
envsubstlà một phần của GNU gettext
Andy

3
@user_mda nơi xuất ra.
derobert

4
Cảnh báo: nếu source.txtchứa bất kỳ $ký tự nào ngoài mở rộng biến, envsubstcũng sẽ thay thế các ký tự này và không có cách nào để thoát khỏi $. Cách giải quyết phổ biến (xấu xí) là export DOLLAR="$".
Kos

60

Đối với câu trả lời 2, khi thảo luận về envsubst, bạn đã hỏi:

Làm cách nào để làm cho nó hoạt động với các biến được khai báo trong tập lệnh .sh của tôi?

Câu trả lời là bạn chỉ cần xuất các biến của mình trước khi gọi envsubst .

Bạn cũng có thể giới hạn các chuỗi biến mà bạn muốn thay thế trong đầu vào bằng cách sử dụng envsubst SHELL_FORMATđối số (tránh việc thay thế ngoài ý muốn của một chuỗi trong đầu vào bằng một giá trị biến shell chung - ví dụ:$HOME ).

Ví dụ:

export VAR1='somevalue' VAR2='someothervalue'
MYVARS='$VAR1:$VAR2'

envsubst "$MYVARS" <source.txt >destination.txt

Sẽ thay thế tất cả các trường hợp của $VAR1$VAR2(và chỉ VAR1VAR2) source.txtbằng 'somevalue''someothervalue'tương ứng.


14
Các dấu nháy đơn 'được sử dụng để thiết lập MYVARSlà rất quan trọng
Đánh dấu Lakata

Lưu ý rằng export, envsubsthoặc bất kỳ lệnh xen kẽ nào có thể không thành công khi văn bản thay thế lớn. Tài liệu tham khảo.
giám mục

@ram đó là do việc bỏ qua SHELL_FORMATđối số (tức là "$MYVARS") khiến tất cả các biến đã xuất được thay thế. Nếu đó là những gì bạn cần, thì đừng lo lắng.
Thiago Figueiro

12

Tôi biết chủ đề này đã cũ, nhưng tôi có một giải pháp làm việc đơn giản hơn mà không cần xuất các biến. Có thể là oneliner, nhưng tôi thích phân chia bằng cách sử dụng \ở đầu dòng.

var1='myVar1'\
var2=2\
var3=${var1}\
envsubst '$var1,$var3' < "source.txt" > "destination.txt"

#         ^^^^^^^^^^^    ^^^^^^^^^^     ^^^^^^^^^^^^^^^
# define which to replace   input            output

Các biến cần được xác định cùng dòng envsubstđể được coi là biến môi trường.

Các '$var1,$var3'là tùy chọn để chỉ thay thế những cái cụ thể. Hãy tưởng tượng một tệp đầu vào chứa tệp ${VARIABLE_USED_BY_JENKINS}không nên được thay thế.


8
  1. Xác định biến ENV của bạn
$ export MY_ENV_VAR=congratulation
  1. Tạo tệp mẫu ( in.txt ) với nội dung sau
$MY_ENV_VAR

Bạn cũng có thể sử dụng tất cả các biến ENV khác do hệ thống của bạn xác định như (trong linux) $ TERM, $ SHELL, $ HOME ...

  1. Chạy lệnh này để thay thế tất cả các biến env trong tệp in.txt của bạn và ghi kết quả vào out.txt
$ envsubst "`printf '${%s} ' $(sh -c "env|cut -d'=' -f1")`" < in.txt > out.txt
  1. Kiểm tra nội dung của tệp out.txt
$ cat out.txt

và bạn sẽ thấy "chúc mừng".


0

Nếu bạn thực sự chỉ muốn sử dụng bash (và sed), thì tôi sẽ xem xét từng biến môi trường của bạn (như được trả về setở chế độ posix) và xây dựng một loạt -e 'regex'cho sed từ đó, được kết thúc bởi một-e 's/\$[a-zA-Z_][a-zA-Z0-9_]*//g' , sau đó chuyển tất cả các biến đó vào quyến rũ.

Perl sẽ làm một công việc tốt hơn, tuy nhiên, bạn có quyền truy cập vào môi trường vars dưới dạng một mảng và bạn có thể thực hiện các thay thế thực thi để bạn chỉ khớp với bất kỳ biến môi trường nào một lần.


tại sao ai đó lại phản đối một câu trả lời như thế này ??? -perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' "$main_tf_file"
Yordan Georgiev

0

Trên thực tế, bạn cần phải thay đổi của bạn readđể read -rlàm cho nó bỏ qua dấu gạch chéo ngược.

Ngoài ra, bạn nên thoát khỏi dấu ngoặc kép và dấu gạch chéo ngược. Vì thế

while read -r line; do
  line="${line//\\/\\\\}"
  line="${line//\"/\\\"}"
  line="${line//\`/\\\`}"
  eval echo "\"$line\""
done > destination.txt < source.txt

Tuy nhiên, vẫn là một cách khủng khiếp để mở rộng.


Trên thực tế, đó là rủi ro khi làm "eval" trên một tập tin có thể chứa mã xấu
Ben

0

Xuất tất cả các biến cần thiết và sau đó sử dụng perl onliner

TEXT=$(echo "$TEXT"|perl -wpne 's#\${?(\w+)}?# $ENV{$1} // $& #ge;')

Điều này sẽ thay thế tất cả các biến ENV có trong TEXT bằng các giá trị thực tế. Trích dẫn cũng được giữ nguyên :)


0

Nếu bạn muốn các biến env được thay thế trong các tệp nguồn của mình trong khi vẫn giữ nguyên tất cả các biến không phải là env, bạn có thể sử dụng lệnh sau:

envsubst "$(printf '${%s} ' $(env | sed 's/=.*//'))" < source.txt > destination.txt

Cú pháp để chỉ thay thế các biến cụ thể được giải thích ở đây . Lệnh trên đang sử dụng một sub-shell để liệt kê tất cả các biến đã xác định và sau đó chuyển nó đếnenvsubst

Vì vậy, nếu có một biến env được xác định được gọi $NAMEsource.txttệp của bạn trông giống như sau:

Hello $NAME
Your balance is 123 ($USD)

Ý destination.txtchí là:

Hello Arik
Your balance is 123 ($USD)

Chú ý rằng cái $NAMEđược thay thế và cái $USDđược giữ nguyên


0

Gọi mã nhị phân perl, trong chế độ tìm kiếm và thay thế trên mỗi dòng (sự -pi) bằng cách chạy mã perl (sự -e) trong các dấu nháy đơn, lặp lại trên các khóa của %ENVhàm băm đặc biệt có chứa tên biến được xuất dưới dạng khóa và giá trị biến được xuất dưới dạng các giá trị của khóa và đối với mỗi lần lặp, hãy thay thế một chuỗi có chứa a $<<key>>bằng chuỗi đó <<value>>.

 perl -pi -e 'foreach $key(sort keys %ENV){ s/\$$key/$ENV{$key}/g}' file

Lưu ý: Cần xử lý logic bổ sung cho các trường hợp trong đó hai hoặc nhiều vars bắt đầu bằng cùng một chuỗi ...


-1

envsubstcó vẻ như chính xác là thứ mà tôi muốn sử dụng, nhưng -vtùy chọn làm tôi ngạc nhiên một chút.

Trong khi envsubst < template.txt động tốt, tùy chọn tương tự -vkhông hoạt động:

$ cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.1 (Maipo)
$ envsubst -V
envsubst (GNU gettext-runtime) 0.18.2
Copyright (C) 2003-2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Bruno Haible.

Như tôi đã viết, điều này không hoạt động:

$ envsubst -v < template.txt
envsubst: missing arguments
$ cat template.txt | envsubst -v
envsubst: missing arguments

Tôi phải làm điều này để làm cho nó hoạt động:

TEXT=`cat template.txt`; envsubst -v "$TEXT"

Có thể nó sẽ giúp ai đó.

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.