sed: làm thế nào để thay thế dòng nếu tìm thấy hoặc nối vào cuối tập tin nếu không tìm thấy?


34

Với một tệp đầu vào duy nhất chỉ chứa các nhận xét (bắt đầu bằng #) và VARIABLE = dòng giá trị, có thể thay thế một giá trị cho một biến duy nhất nếu được tìm thấy và, nếu không, sẽ thêm cặp vào cuối tệp nếu không tìm thấy?

Phương thức hiện tại của tôi hoạt động bằng cách xóa nó trong một lượt đầu tiên, sau đó nối nó vào cuối tập tin trong lần thứ hai, nhưng phương thức này làm rối trật tự dòng (và cũng là hai lệnh khác nhau):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

Có dù sao để làm điều này, tức là. giữ trật tự dòng, trong một dòng sed duy nhất? Nếu một số tiện ích khác (awk, ...) thực hiện điều này, tôi sẽ sử dụng nó trên sed.

Câu trả lời:


29

Nó thực sự khá đơn giản với sed: nếu một dòng khớp chỉ cần sao chép nó vào hkhông gian cũ thì sẽ stạo ra giá trị.
Trên dòng la $t xthay đổi giữ không gian và không gian mẫu sau đó kiểm tra xem cái sau có trống không. Nếu nó không trống, điều đó có nghĩa là sự thay thế đã được thực hiện nên không có gì để làm. Nếu nó trống, điều đó có nghĩa là không tìm thấy kết quả khớp nào, vì vậy hãy thay thế không gian mẫu bằng biến = value mong muốn, sau đó nối vào dòng hiện tại trong bộ đệm giữ. Cuối cùng, e xlại thay đổi:

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

Trên đây là gnu sedcú pháp. Di động:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile

1
Làm thế nào điều này sẽ trông như thế nào nếu newvalueđược lưu trữ trong một biến?
user1810087

sed "/^${varName}=/{h;s/=.*/=${varValue}/};\${x;/^$/{s//${varName}=${varValue}/;H};x}" ${VARFILE}với các biến, dấu ngoặc kép để cho phép thay thế biến và thoát khỏi $một thay thế không phải là sự thay thế tại thời điểm này, ngoài ra nếu giá trị của bạn được lưu trữ trong $$varName:sed "/^${varName}=/{h;s/=.*/=${!varName}/};\${x;/^$/{s//${varName}=${!varName}/;H};x}" ${VARFILE}
DarkMukke

1
Giải pháp này không 'đơn giản' lol. Nhưng nó đã có tác dụng. Các trang sed man không phải là những thứ dễ dàng nhất để sàng lọc, nó sẽ thực sự hữu ích nếu bạn có thể chi tiết hóa hiệu ứng của mọi phần của biểu thức :)
user2066480

18

Điều này có thể được rút ngắn. Đây không phải là một lệnh sed đơn lẻ và nó cũng sử dụng grep, nhưng đây dường như là những gì bạn muốn. Đó là một dòng duy nhất và nó chỉnh sửa tệp tại chỗ (không có tệp tạm thời).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file

13

Đây là một sedcách tiếp cận đơn giản hơn , vì tôi không thấy sed hold spacedễ làm việc. Nếu bạn cảm thấy thoải mái hold space, sử dụng phương pháp don_crissti mang lại cơ hội bổ sung để bảo tồn mọi thứ khỏi dòng hiện có, nhưng điều này thường rất hiếm.

Trong phương pháp này, bạn chỉ cần in tất cả trừ dòng mà bạn muốn thả và cuối cùng, nối thêm phần thay thế.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile

1
Phiên bản này rất tốt nếu bạn dự định cập nhật một tệp có thể có giá trị hiện có nhưng lỗi thời.
guillem

3

Dựa trên các câu trả lời khác, nếu điều bạn muốn làm là thay thế giá trị của biến nếu biến đó có trong tệp và nối nó vào cuối tệp nếu không phải (đó không phải là sedlệnh đã đăng của bạn ), bạn có thể thử điều này:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file

1

Thật dễ dàng hơn một chút trong awk, mặc dù "chỉnh sửa tại chỗ" không tự động:

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file

1

Chỉ cần sử dụng grepechođể tạo một bản ghi trống:

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

Mỗi dòng thoát với mã lỗi bằng không.


Bạn mất trật tự dòng trong khi thêm một lệnh bổ sung grepecho
BlakBat

Thật vậy, nếu bạn chèn các mục mới vào cuối tệp, nhưng hãy tiếp tục đặt hàng nếu bạn thay thế một giá trị cũ.
MUY Bỉ

0

Thực tế hoạt động với những điều sau đây: sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile Trong câu trả lời choosen -i bị thiếu.


3
Đây thực sự là một bình luận và không phải là một câu trả lời cho câu hỏi ban đầu. Để phê bình hoặc yêu cầu làm rõ từ một tác giả, hãy để lại nhận xét bên dưới bài đăng của họ - bạn luôn có thể nhận xét về bài đăng của riêng bạn và khi bạn có đủ danh tiếng, bạn sẽ có thể nhận xét về bất kỳ bài đăng nào . Xin vui lòng đọc tại sao tôi cần 50 danh tiếng để bình luận? Tôi có thể làm gì thay thế?
DavidPostill

-1

để làm điều này với perl và tại chỗ, điều này hoạt động tốt với tôi:

grep ^FOOBAR= my.file && perl -i -ple "s/^FOOBAR=.+/FOOBAR=newvalue/g" my.file || echo FOOBAR=newvalue >> my.file

2
Câu trả lời này là xấu theo nhiều cách! grepechoperlthay vì làm tất cả mọi thứ trong perl? Ngoài ra, viết vào một tệp ( >> my.file) khi bạn đang đọc từ nó ( grep my.file)?
vladr
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.