2010-04-23 29 views
6

Tôi cần viết một kịch bản đơn giản để thay thế khối văn bản trong tệp cấu hình bằng nội dung của tệp khác.Thay thế khối văn bản được phân tách trong tập tin bằng nội dung của một tệp khác

Giả sử với có các tập tin đơn giản sau đây:

server.xml

<?xml version='1.0' encoding='UTF-8'?> 
<Server port="8005" shutdown="SHUTDOWN"> 
    <Service name="Catalina"> 
    <Connector port="80" protocol="HTTP/1.1"/> 
    <Engine name="Catalina" defaultHost="localhost"> 
     <!-- BEGIN realm --> 
     <sometags/> 
     <sometags/> 
     <!-- END realm --> 
     <Host name="localhost" appBase="webapps"/> 
    </Engine> 
    </Service> 
</Server> 

realm.xml

<Realm className="org.apache.catalina.realm.UserDatabaseRealm" 
     resourceName="UserDatabase"/> 

Tôi muốn chạy một kịch bản và có realm.xml thay thế nội dung giữa <!-- BEGIN realm --><!-- END realm --> dòng. Nếu realm.xml thay đổi thì bất cứ khi nào tập lệnh được chạy lại, nó sẽ thay thế lại các dòng với nội dung mới là realm.xml. Điều này dự định được chạy trong /etc/init.d/tomcat khi khởi động dịch vụ trên nhiều cài đặt mà trên đó lĩnh vực sẽ khác.

Tôi không chắc chắn làm cách nào tôi có thể thực hiện việc này đơn giản chỉ với awk hoặc sed.

Trả lời

12

này cung cấp cho một thử:

sed -i -ne '/<!-- BEGIN realm -->/ {p; r realm.xml' -e ':a; n; /<!-- END realm -->/ {p; b}; ba}; p' server.xml 
+0

Whoa ... nó hoạt động. Tôi đang cố gắng để có được một hang của phân nhánh để thực sự hiểu những gì đang xảy ra. – rmarimon

+3

Nhánh 'ba' để gắn nhãn" a "bên trong dấu ngoặc kết hợp với phép thử cho" BEGIN "và các nhánh' b' đến cuối khi "END" được tìm thấy vì nó nằm trong bộ dấu ngoặc kết hợp với phép thử đó. Nó giống như 'if/BEGIN/sau đó đọc tệp; trong khi không/END/do bỏ qua dòng'. –

+0

Tôi gặp lỗi cú pháp với điều này: 'sed: -e expression # 1, char 39: unexpected '}'' –

3
TOTAL_LINES=`cat server.xml | wc -l` 
BEGIN_LINE=`grep -n -e '<!-- BEGIN realm -->' server.xml | cut -d : -f 1` 
END_LINE=`grep -n -e '<!-- END realm -->' server.xml | cut -d : -f 1` 
TAIL_LINES=$(($TOTAL_LINES-$END_LINE)) 

head -n $BEGIN_LINE server.xml > server2.xml 
cat realm.xml > server2.xml 
tail -n $TAIL_LINES server.xml > server2.xml 

(OK, điều này không sử dụng awk hoặc sed ... tôi cho rằng đó không phải là một yêu cầu độc quyền :-)

+0

Đó không phải là yêu cầu độc quyền ;-) – rmarimon

+0

Tính năng này có hoạt động không? TOTAL_LINES sẽ có một giá trị bao gồm chuỗi "server.xml" trong hầu hết các phiên bản của wc, vì vậy tôi nghi ngờ số học sẽ không thành công. –

+0

@William Pursell - điểm tốt, cố định. –

2

bạn có thể sử dụng awk

awk 'FNR==NR{ _[++d]=$0;next} 
/BEGIN realm/{ 
    print 
    for(i=1;i<=d;i++){ print _[i] } 
    f=1;next 
} 
/END realm/{f=0}!f' realm.xml server.xml > temp && mv temp server.xml 

realm.xml là chuyển đến awk như tập tin đầu tiên. FNR == NR có nghĩa là nhận được hồ sơ của tập tin đầu tiên được chuyển vào và lưu vào biến số _. awk sẽ xử lý tệp tiếp theo khi FNR! = NR. nếu awk tìm thấy /BEGIN realm/, in dòng BEGIN realm, sau đó in những gì được lưu trữ trong _. Bằng cách đặt cờ (f) thành 1, phần còn lại của các dòng sau BEGIN realm sẽ không được in cho đến khi phát hiện thấy /END realm/.

+0

Điều này có vẻ giống như cách tiếp cận đúng nhưng nó rất khó hiểu. Bạn có thể cung cấp một số manh mối về cách thức hoạt động của nó không? – rmarimon

+0

Làm thế nào để thay đổi điều này để nó có thể thay thế tại chỗ như "sed -i"? – rmarimon

+0

bạn chỉ cần chuyển hướng đến tệp tạm thời và đổi tên nó trở lại. – ghostdog74

1

Làm thế nào về đoạn này ít tôi đã tạo:

sed -n \ 
    -e "1,/<\!-- BEGIN realm -->/ p" \ 
    -e"/<\!-- END realm -->/,$ p" \ 
    -e "/<\!-- BEGIN realm -->/ r realm.xml" \ 
    server.xml 

Các lệnh đầu tiên in các dòng lên đến <!- BEGIN realm --> lệnh in thứ hai dòng bắt đầu từ <!-- END realm --> và các lệnh thứ ba thêm văn bản trong tập tin 'vương quốc. xml '. Nếu chỉ tôi có thể đơn giản hóa việc loại bỏ các đường giữa <!- BEGIN realm --><!-- END realm --> mà không xóa các đường đánh dấu nó sẽ đơn giản như nó được. Và nó có thể được thực hiện inplace với sed !!!

+0

điều gì về ' '? lệnh sed của bạn không thay thế ' '. – ghostdog74

+1

Khi tôi chạy nó trong máy linux của tôi nó. Hơn nữa, nếu bạn chạy lệnh không có tập lệnh cuối cùng (-e), nó cung cấp cho '' server.xml'' mà không có tất cả '' ''. – rmarimon

+0

Không hoạt động trên Ubuntu chính xác. Chèn văn bản nhưng không xóa ... –

0

Tôi chạy vào cùng một nhu cầu này (do đó việc tìm kiếm câu hỏi này). Sau khi đùa giỡn với sed và awk quá lâu, tôi cuối cùng đã nhận ra không có gì sai với việc sử dụng một, dễ đọc, dễ hiểu, ngôn ngữ phổ biến rộng rãi hiện đại như Python:

python <<EOF 
    import os, sys, re 
    fname = 'server.xml' 
    os.rename(fname, fname + '.orig') 
    with open(fname + '.orig', 'r') as fin, open(fname, 'w') as fout: 
     data = fin.read() 

     data = re.sub(r'(<!-- BEGIN realm -->).*?(<!-- END realm -->)', 
      r'\1\n' + 
      'insert whatever you want here\n' + 
      r'\2\n', data, flags=re.DOTALL) 
     fout.write(data) 
    EOF 

Tôi nghĩ sed và awk đã có ngày của họ.Chúng hữu ích một lần vào một thời gian, nhưng rất ít người có thể đọc hoặc viết mà không cần sự trợ giúp tài liệu trong những ngày này.

(Nguồn: internet)

0

tôi đã không thể có được giải pháp Dennis dễ dàng làm việc trên OS X (sed BSD của nó là hơi khác nhau). Tôi tìm thấy giải pháp khác mà tôi đã có thể làm việc trên cả Linux và OS X (Tôi có một môi trường hỗn hợp). Phiên bản gốc trên superuser.com chỉ hoạt động trên Linux, ở đây tôi cố định nó:

lead='^<!-- BEGIN realm -->$' 
tail='^<!-- END realm -->' 
sed -e '/'"$lead"'/,/'"$tail"'/{ /'"$lead"'/{p; r realm.xml' -e' }; /'"$tail"'/p; d;} ' server.xml 

Dưới đây là một phiên bản của mã Dennis rằng cũng hoạt động trên OS X (sử dụng nhiều dòng):

sed -ne '/'"$lead"'/ { 
p 
r realm.xml 
:a 
n 
/'"$tail"'/ { 
    p 
    b 
} 
ba 
} 
p' server.xml 

Cả hai mã in đầu ra trên thiết bị xuất chuẩn. Sử dụng chuyển hướng hoặc, để thay thế tệp nội tuyến, thêm tùy chọn '-i' (trên Linux) hoặc '-i' "'(trên BSD/OS X).

Các vấn đề liên quan