Tìm và thay thế trong tệp và ghi đè lên tệp không hoạt động, nó làm trống tệp


604

Tôi muốn chạy tìm và thay thế trên tệp HTML thông qua dòng lệnh.

Lệnh của tôi trông giống như thế này:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

Khi tôi chạy cái này và nhìn vào tập tin sau đó, nó trống rỗng. Nó xóa nội dung tập tin của tôi.

Khi tôi chạy nó sau khi khôi phục lại tệp:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Các stdoutlà nội dung của tập tin, và tìm và thay thế đã được thực hiện.

Tại sao chuyện này đang xảy ra?


13
Thay thế Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski

nhiều sedlệnh liên quan để tìm một chuỗi và thay thế toàn bộ dòng: stackoverflow.com/questions/11245144/
Đổi

Câu trả lời:


917

Khi shell nhìn thấy > index.htmltrong dòng lệnh, nó sẽ mở tệp index.htmlđể ghi , xóa sạch tất cả nội dung trước đó của nó.

Để khắc phục điều này, bạn cần chuyển -itùy chọn để sedthực hiện các thay đổi nội tuyến và tạo bản sao lưu của tệp gốc trước khi thực hiện thay đổi tại chỗ:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Không có .bak, lệnh sẽ thất bại trên một số nền tảng, chẳng hạn như Mac OSX.


20
Nói truncates the filethay vì opens the filecó lẽ làm cho nó rõ ràng hơn.
Mikel

12
Ít nhất là trên máy Mac của tôi, đề xuất đầu tiên không hoạt động ... nếu bạn đang thực hiện thay thế tại chỗ trên một tệp, bạn phải chỉ định tiện ích mở rộng. Bạn có thể, ít nhất, vượt qua trong một phần mở rộng zero-length mặc dù: sed -i '' s / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza

5
cho các biến sed -i.bak 's /' $ search '/' $ thay thế '/ g' index.html
Fatima Zohra

33
trên osx, sử dụng một chuỗi rỗng '' làm tham số cho -i, như:sed -i '' 's/blah/xx/g'
Pierre Houston

4
Nhưng bạn là gì .baksau sed -i?
Patrizio Bertoni

210

Một mô hình thay thế, hữu ích là:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

Điều đó có nhiều tác dụng tương tự, mà không sử dụng -itùy chọn, và điều đó cũng có nghĩa là, nếu vì lý do nào đó, kịch bản sed không thành công, tệp đầu vào không bị ghi đè. Hơn nữa, nếu chỉnh sửa thành công, không có tệp sao lưu nào nằm xung quanh. Loại thành ngữ này có thể hữu ích trong Makefiles.

Khá nhiều sed có -itùy chọn, nhưng không phải tất cả chúng; sed posix là một trong đó không. Do đó, nếu bạn đang nhắm đến tính di động, tốt nhất nên tránh.


9
+1 không có tệp sao lưu nằm xung quanh và không ghi đè tệp đầu vào nếu chỉnh sửa thất bại. Làm việc hoàn hảo trên mac.
Mike Grace

Làm việc cho tôi hoàn hảo. Cảm ơn bạn! (trên máy Mac)
quan tâm

1
Điều này làm việc cho tôi một cách hoàn hảo khi trên Ubuntu Server 14.04 sed -i không xóa tập tin.
Chris Giddings

2
Tăng cường cực kỳ nhỏ:... && mv index.html{.tmp,}
EdwardGarson

5
@EdwardGarson Thật vậy, đó có lẽ là những gì tôi sẽ sử dụng nếu tôi gõ nó - Tôi đồng ý rằng nó gọn gàng hơn - nhưng sh(nếu tôi nhớ chính xác) không có bản {...}mở rộng đó . Trong Makefile bạn có thể đang sử dụng shchứ không phải bash, vì vậy nếu bạn đang nhắm đến tính di động (hoặc tính tích cực) thì bạn sẽ cần tránh việc xây dựng đó.
Norman Gray

95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Điều này thực hiện thay thế tại chỗ toàn cầu trên tệp index.html. Trích dẫn chuỗi ngăn ngừa các vấn đề với khoảng trắng trong truy vấn và thay thế.


57

sử dụng tùy chọn sedi -i, vd

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html

Điều đó có nghĩa là gì? sed: -i có thể không được sử dụng với stdin
sheetal

2
Nhớ bao quanh mẫu của bạn trong dấu ngoặc kép nếu nó chứa khoảng trắng -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson

@sheetal: -ithực hiện chỉnh sửa tại chỗ các tệp , vì vậy sẽ không hợp lý khi kết hợp nó với đầu vào stdin .
mkuity0

Điều này có thể hoạt động trên macOS, nhưng nó không có trên Arch Linux đối với tôi.
xdevs23

Không có -e, câu trả lời được chấp nhận không hoạt động trên MacOS, Catalina. Với -e nó hoạt động.
cwhiii

18

Để thay đổi nhiều tệp (và lưu bản sao lưu của mỗi tệp dưới dạng * .bak):

perl -p -i -e "s/\|/x/g" *  

sẽ lấy tất cả các tập tin trong thư mục và thay thế |bằng x cái này được gọi là một miếng bánh Perl (dễ như một chiếc bánh)


1
Thật tốt khi thấy ai đó sẵn sàng xem xét các tuyên bố vấn đề, và không chỉ các thẻ. OP không chỉ định sednhư một yêu cầu, chỉ sử dụng nó như là công cụ đã thử.
dùng7412956

14

Bạn nên thử sử dụng tùy chọn -iđể chỉnh sửa tại chỗ.


6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Nếu bạn có một liên kết được thêm vào, hãy thử điều này. Tìm kiếm URL như trên (bắt đầu bằng https và kết thúc bằng.com tại đây) và thay thế bằng chuỗi URL. Tôi đã sử dụng một biến $pub_urlở đây. sở đây có nghĩa là tìm kiếm và gcó nghĩa là thay thế toàn cầu.

Nó hoạt động!


6

Cảnh báo: đây là một phương pháp nguy hiểm! Nó lạm dụng bộ đệm i / o trong linux và với các tùy chọn cụ thể của bộ đệm, nó quản lý để làm việc trên các tệp nhỏ. Đó là một sự tò mò thú vị. Nhưng đừng sử dụng nó cho một tình huống thực tế!

Bên cạnh -itùy chọn của sed bạn có thể sử dụng teetiện ích .

Từ man:

tee - đọc từ đầu vào tiêu chuẩn và ghi vào đầu ra và tập tin tiêu chuẩn

Vì vậy, giải pháp sẽ là:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- ở đây teeđược lặp lại để đảm bảo rằng đường ống được đệm. Sau đó, tất cả các lệnh trong đường ống bị chặn cho đến khi chúng nhận được một số đầu vào để làm việc. Mỗi lệnh trong đường ống bắt đầu khi các lệnh ngược dòng đã ghi 1 bộ đệm byte (kích thước được xác định ở đâu đó ) vào đầu vào của lệnh. Vì vậy, lệnh cuối cùng tee index.html, mở tệp để ghi và do đó làm trống nó, chạy sau khi đường ống ngược dòng kết thúc và đầu ra nằm trong bộ đệm trong đường ống.

Nhiều khả năng sau đây sẽ không hoạt động:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- nó sẽ chạy cả hai lệnh của đường ống cùng một lúc mà không bị chặn. (Nếu không ngăn chặn các đường ống phải vượt qua dòng byte bởi dòng thay vì đệm bằng đệm. Tương tự như khi bạn chạy cat | sed s/bar/GGG/. Nếu không ngăn chặn nó tương tác nhiều hơn và thường đường ống chỉ 2 lệnh chạy mà không có đệm và ngăn chặn. Đường ống dài hơn được đệm.) Các tee index.htmlý chí mở tập tin để viết và nó sẽ được làm trống. Tuy nhiên, nếu bạn luôn bật bộ đệm, phiên bản thứ hai cũng sẽ hoạt động.


3
Tệp đầu ra của tee cũng được mở ngay lập tức dẫn đến một index.html trống cho toàn bộ lệnh.
sjngm

3
Điều này sẽ làm hỏng bất kỳ tệp đầu vào nào lớn hơn bộ đệm đường ống (thường là 64KB) . (@sjngm: tệp không bị cắt ngay lập tức như với >, nhưng vấn đề là đó là một giải pháp bị hỏng có khả năng dẫn đến mất dữ liệu).
mkuity0

4

Vấn đề với lệnh

sed 'code' file > file

cái đó filebị cắt bởi cái vỏ trước khi sed thực sự xử lý nó. Kết quả là bạn nhận được một tập tin trống.

Cách sed để làm điều này là sử dụng -iđể chỉnh sửa tại chỗ, như các câu trả lời khác được đề xuất. Tuy nhiên, đây không phải lúc nào cũng là điều bạn muốn. -isẽ tạo một tệp tạm thời sau đó sẽ được sử dụng để thay thế tệp gốc. Đây là vấn đề nếu tệp gốc của bạn là một liên kết (liên kết sẽ được thay thế bằng một tệp thông thường). Nếu bạn cần bảo toàn các liên kết, bạn có thể sử dụng một biến tạm thời để lưu trữ đầu ra của sed trước khi ghi lại vào tệp, như sau:

tmp=$(sed 'code' file); echo -n "$tmp" > file

Hơn thế nữa, việc sử dụng printfthay vì echokể từ khi echocó khả năng xử lý \\như \ở một số vỏ (ví dụ như dấu gạch ngang):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file

1
+1 để bảo toàn liên kết. Nó cũng hoạt động với một tệp tạm thời:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha

3

edcâu trả lời:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

Để nhắc lại những gì codaddict đã trả lời , shell sẽ xử lý chuyển hướng trước , xóa sạch tệp "input.html" và sau đó shell gọi lệnh "sed" chuyển cho nó một tệp trống.


2
Câu hỏi nhanh, tại sao mọi người cứ đưa ra " edphiên bản" của sedcâu trả lời? nó thực hiện nhanh hơn?
cregox

6
Một số sedkhông thực hiện -iđể chỉnh sửa tại chỗ. edcó mặt khắp nơi và không cho phép bạn lưu các chỉnh sửa của mình vào tệp gốc. Ngoài ra, thật tốt khi có nhiều công cụ trong bộ của bạn.
glenn jackman

ok, tuyệt Vì vậy, hiệu suất khôn ngoan, chúng giống như tôi cho rằng. cảm ơn!
cregox

2

Bạn có thể sử dụng Vim trong chế độ Ex:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % chọn tất cả các dòng

  2. x lưu và đóng


0

Tôi đã tìm kiếm tùy chọn nơi tôi có thể xác định phạm vi dòng và tìm thấy câu trả lời. Ví dụ tôi muốn thay đổi host1 thành host2 từ dòng 36-57.

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

Bạn cũng có thể sử dụng tùy chọn gi để bỏ qua trường hợp ký tự.

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

0

Với tất cả sự tôn trọng với các câu trả lời đúng ở trên, luôn luôn là một ý tưởng tốt để "chạy khô" các tập lệnh như vậy, để bạn không làm hỏng tệp của mình và phải bắt đầu lại từ đầu.

Chỉ cần lấy tập lệnh của bạn để đổ đầu ra vào dòng lệnh thay vì ghi nó vào tệp, ví dụ như thế:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

HOẶC LÀ

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

Bằng cách này bạn có thể xem và kiểm tra đầu ra của lệnh mà không bị cắt tệp.

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.