Chỉ thêm một dòng vào một tệp nếu nó chưa tồn tại


157

Tôi cần thêm dòng sau vào cuối tập tin cấu hình:

include "/configs/projectname.conf"

đến một tập tin gọi là lighttpd.conf

Tôi đang xem xét sử dụng sedđể làm điều này, nhưng tôi không thể tìm ra cách.

Làm thế nào tôi chỉ chèn nó nếu dòng không tồn tại?


Nếu bạn đang cố gắng chỉnh sửa một tập tin ini, công cụ crudinicó thể là một lựa chọn tốt (nhưng chưa phải cho lighthttpd)
rubo77

Câu trả lời:


290

Chỉ cần giữ cho nó đơn giản :)

grep + echo nên đủ:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

Chỉnh sửa: kết hợp các đề xuất @cerin và @ thijs-wouters .


2
Tôi sẽ thêm công tắc -q vào grep để chặn đầu ra: grep -vq ...
Dave Kirby

1
FYI, sử dụng -v và && không muốn thực hiện mánh khóe, trong khi | | không có -v hoạt động.
bPizzi

3
Điều này chỉ hoạt động cho các dòng rất đơn giản. Mặt khác, grep diễn giải hầu hết các ký tự không phải alpha trong dòng của bạn dưới dạng mẫu, khiến nó không phát hiện được dòng trong tệp.
Cerin

2
Giải pháp đẹp. Điều này cũng hoạt động để kích hoạt các biểu thức phức tạp hơn, tất nhiên. Mine sử dụng echođể kích hoạt một di cattruyền đa dòng vào một tệp cấu hình.
Eric L.

2
Thêm -sđể bỏ qua lỗi khi tệp không tồn tại, tạo một tệp mới chỉ với dòng đó.
Frank

78

Đây sẽ là một giải pháp sạch, dễ đọc và có thể sử dụng lại bằng cách sử dụng grepechochỉ thêm một dòng vào một tệp nếu nó chưa tồn tại:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

Nếu bạn cần phải phù hợp với toàn bộ dòng sử dụng grep -xqF

Thêm -sđể bỏ qua lỗi khi tệp không tồn tại, tạo một tệp mới chỉ với dòng đó.


5
Nếu đó là tệp mà bạn cần quyền root:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa

Điều này thực sự không hoạt động khi dòng bắt đầu bằng a -.
hyperjack 6/2/18

@zsero: điểm tốt! Tôi đã thêm --vào lệnh grep, vì vậy nó sẽ không diễn giải $ LINE bắt đầu bằng dấu gạch ngang dưới dạng tùy chọn nữa.
rubo77

13

Đây là một sedphiên bản:

sed -e '\|include "/configs/projectname.conf"|h; ${x;s/incl//;{g;t};a\' -e 'include "/configs/projectname.conf"' -e '}' file

Nếu chuỗi của bạn nằm trong một biến:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file

Đối với lệnh thay thế một phần chuỗi bằng mục nhập tệp cấu hình đầy đủ / cập nhật hoặc nối thêm dòng nếu cần:sed -i -e '\|session.*pam_mkhomedir.so|h; ${x;s/mkhomedir//;{g;tF};a\' -e 'session\trequired\tpam_mkhomedir.so umask=0022' -e '};:F;s/.*mkhomedir.*/session\trequired\tpam_mkhomedir.so umask=0022/g;' /etc/pam.d/common-session
bgStack15

Thật tuyệt, nó đã giúp tôi rất nhiều +1. bạn có thể vui lòng, giải thích biểu hiện sed của bạn?
Rahul

Tôi muốn thấy một lời giải thích có chú thích về giải pháp này. Tôi đã sử dụng sedtrong nhiều năm nhưng chủ yếu chỉ là slệnh. Sự hoán đổi không gian holdpatternkhông gian nằm ngoài tôi.
miền bắc

11

Nếu ghi vào một tệp được bảo vệ, câu trả lời của @drAlberT và @ rubo77 có thể không hiệu quả với bạn vì người ta không thể sudo >>. Một giải pháp đơn giản tương tự , sau đó, sẽ được sử dụng tee --append(hoặc, trên hệ điều hành MacOS, tee -a):

LINE='include "/configs/projectname.conf"'
FILE=lighttpd.conf
grep -qF "$LINE" "$FILE"  || echo "$LINE" | sudo tee --append "$FILE"

6

Nếu, một ngày nào đó, người khác phải xử lý mã này là "mã kế thừa", thì người đó sẽ biết ơn nếu bạn viết mã ít công khai hơn, chẳng hạn như

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi

1
Xin lỗi, nếu bạn không biết shell, thì hãy đi và quản trị Windows. Các giải pháp sử dụng && và || là cú pháp shell bình thường và nên được hiểu bởi bất kỳ quản trị viên có thẩm quyền nào. Không có gì bí truyền cả.
Graham Nicholls

3
Cảm ơn bình luận của bạn Graham! Vâng, đó là cú pháp shell bình thường. Và vâng, bất kỳ quản trị viên có thẩm quyền nên hiểu nó. Tuy nhiên, nó hoạt động như vậy dựa trên tác dụng phụ của thuật toán đánh giá biểu thức trong shell. Vì vậy, bạn có thể gọi tất cả những gì bạn thích, ngoại trừ đơn giản - đó là điểm ban đầu của tôi.
Marcelo Ventura


6

một giải pháp sed khác là luôn luôn nối nó trên dòng cuối cùng và xóa một cái đã có sẵn.

sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"

"Thoát đúng" có nghĩa là đặt một biểu thức chính phù hợp với mục nhập của bạn, nghĩa là để thoát tất cả các điều khiển biểu thức chính khỏi mục nhập thực tế của bạn, nghĩa là đặt dấu gạch chéo ngược trước ^ $ / *? + ().

điều này có thể thất bại ở dòng cuối cùng của tệp của bạn hoặc nếu không có dòng mới nào bị treo, tôi không chắc, nhưng điều đó có thể được xử lý bằng cách phân nhánh tiện lợi ...


1
Đẹp một lót, ngắn và dễ đọc. Hoạt động tuyệt vời để cập nhật vv / máy chủ:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos

Một phiên bản nhỏ hơn:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller

@Jezz Tôi nghĩ rằng điều này sẽ thất bại, nếu mục nhập đã ở cuối tập tin, vì dlệnh sẽ khởi động lại chương trình sed và do đó bỏ qua append, nếu việc xóa xảy ra ở dòng cuối cùng.
Robin479

@ Robin479 Chết tiệt, ppend aphải được thực hiện trước khi delete : sed -i -e '$a<entry>' -e '/<entry>/d'. Nó gần giống với câu trả lời ban đầu của bạn.
Jérôme Pouiller

3

sử dụng awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file

Đó là 'tập tin' hay chỉ là 'tập tin'?
webdevguy

Đó là file filebởi vì nó thực hiện hai lần vượt qua cùng một tệp. NR==FNRlà đúng trên đường chuyền đầu tiên, nhưng không phải là lần thứ hai. Đây là một thành ngữ Awk phổ biến.
tripleee 15/03/2017

1

Các câu trả lời sử dụng grep là sai. Bạn cần thêm tùy chọn -x để khớp với toàn bộ dòng nếu không các dòng như #text to addsẽ vẫn khớp khi muốn thêm chính xác text to add.

Vì vậy, giải pháp chính xác là một cái gì đó như:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

1

Sử dụng sed: Nó sẽ chèn vào cuối dòng. Tất nhiên bạn cũng có thể chuyển các biến như bình thường.

grep -qxF "port=9033" $light.conf
if [ $? -ne 0 ]; then
  sed -i "$ a port=9033" $light.conf
else
    echo "port=9033 already added"
fi

Sử dụng oneliner sed

grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf

Sử dụng echo có thể không hoạt động dưới quyền root, nhưng sẽ hoạt động như thế này. Nhưng nó sẽ không cho phép bạn tự động hóa mọi thứ nếu bạn đang muốn làm điều đó vì nó có thể yêu cầu mật khẩu.

Tôi đã gặp sự cố khi cố gắng chỉnh sửa từ thư mục gốc cho một người dùng cụ thể. Chỉ cần thêm $usernametrước đó là một sửa chữa cho tôi.

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi

Chào mừng bạn đến với stackoverflow! Vui lòng đọc kỹ câu hỏi trước khi đăng câu trả lời - OP đã hỏi cách chèn bằng sed
Markoorn

-1

Tôi cần chỉnh sửa một tập tin với quyền hạn chế ghi rất cần thiết sudo. làm việc từ câu trả lời của ghostdog74 và sử dụng tệp tạm thời:

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file > /tmp/file
sudo mv /tmp/file file

Điều này có vẻ không chính xác, nó sẽ mất các dòng khác từ tệp khi thiếu dòng cấu hình.
tripleee 15/03/2017
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.