sed chỉnh sửa tập tin tại chỗ


300

Tôi cố gắng để tìm hiểu xem nó có thể chỉnh sửa một tập tin trong một lệnh sed duy nhất mà không bằng tay streaming các nội dung đã chỉnh sửa vào một tập tin mới và sau đó đổi tên file mới với tên file gốc. Tôi đã thử -itùy chọn nhưng hệ thống Solaris của tôi nói rằng đó -ilà một lựa chọn bất hợp pháp. Có một cách khác nhau?


7
-ilà một tùy chọn trong gnu sed, nhưng không phải trong sed tiêu chuẩn. Tuy nhiên, nó truyền nội dung vào một tệp mới và sau đó đổi tên tệp để nó không như bạn muốn.
William Pursell

2
thực ra, đó là những gì tôi muốn, tôi chỉ muốn không bị phơi bày việc phải thực hiện nhiệm vụ trần tục là đổi tên tập tin mới thành tên gốc
amphibient

3
Sau đó, bạn cần phải đặt lại câu hỏi.
William Pursell

3
@amphibient: Bạn có phiền chút nào về tiền tố tiêu đề của câu hỏi của bạn với từ 'Solaris' không? Giá trị của câu hỏi của bạn đang bị mất. Xin vui lòng xem các ý kiến ​​dưới câu trả lời của tôi. Cảm ơn.
Steve

1
@Steve: Tôi đã xóa tiền tố Solaris khỏi tiêu đề một lần nữa bởi vì đây không phải là độc quyền của Solaris.
tripleee

Câu trả lời:


477

Các -itùy chọn suối nội dung đã chỉnh sửa vào một tập tin mới và sau đó đổi tên nó đằng sau hậu trường, dù sao.

Thí dụ:

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

trên macOS .


3
ít nhất là nó làm điều đó cho tôi vì vậy tôi không phải
lưỡng cư

2
Sau đó, bạn có thể cố gắng biên dịch GNU sed trên hệ thống của mình.
choroba

3
ví dụ:sed -i "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>
Thales Ceolin

2
Điều này là tốt hơn so với giải pháp perl. Bằng cách nào đó nó không tạo ra bất kỳ tập tin .swap nào .... cảm ơn!
toán

3
@maths: Nó có, nhưng có thể ở một nơi khác hoặc trong một thời gian ngắn hơn?
choroba

72

Trên một hệ thống sedkhông có khả năng chỉnh sửa các tệp tại chỗ, tôi nghĩ giải pháp tốt hơn sẽ là sử dụng perl:

perl -pi -e 's/foo/bar/g' file.txt

Mặc dù điều này không tạo ra một tập tin tạm thời, nó thay thế bản gốc vì một hậu tố / phần mở rộng trống đã được cung cấp.


14
Nó không chỉ tạo ra một tệp tạm thời, nó còn phá vỡ các liên kết cứng (ví dụ, thay vì thay đổi nội dung của tệp, nó thay thế tệp bằng một tệp mới cùng tên.) Đôi khi, đây là hành vi mong muốn, đôi khi nó là chấp nhận được, nhưng khi có nhiều liên kết đến một tệp thì hầu như luôn luôn là hành vi sai.
William Pursell

3
@DwightSpencer: Tôi tin rằng tất cả các hệ thống không có Perl đều bị hỏng. Một lệnh duy nhất bỏ qua một chuỗi các lệnh khi muốn có quyền nâng cao và phải sử dụng sudo. Trong tâm trí của tôi, câu hỏi là về cách tránh tự tạo và đổi tên tệp tạm thời mà không phải cài đặt GNU hoặc BSD mới nhất sed. Chỉ cần sử dụng Perl.
Steve

4
@PetrPeller: Thật sao? Nếu bạn đọc kỹ câu hỏi, bạn sẽ hiểu rằng OP đang cố tránh điều gì đó như , sed 's/foo/bar/g' file.txt > file.tmp && mv file.tmp file.txt. Chỉ vì chỉnh sửa tại chỗ thực hiện đổi tên bằng tệp tạm thời, không có nghĩa là anh ấy / cô ấy phải thực hiện đổi tên thủ công khi tùy chọn không khả dụng. Có những công cụ khác có thể làm những gì anh ấy / cô ấy muốn, và Perl là sự lựa chọn rõ ràng. Làm thế nào đây không phải là một câu trả lời cho câu hỏi ban đầu?
Steve

2
@a_arias: Bạn nói đúng; anh ta đã làm. Nhưng anh ta không thể đạt được những gì anh ta muốn bằng Solaris sed. Câu hỏi thực sự là một câu hỏi rất hay, nhưng giá trị của nó đã bị giảm đi bởi những người không thể đọc trang người đàn ông hoặc không thể bận tâm để thực sự đọc câu hỏi ở đây trên SO.
Steve

4
@EdwardGarson: IIRC, Solaris giao hàng với Perl nhưng không phải Python hay Ruby. Và như tôi đã nói trước đây, OP không thể đạt được kết quả mong muốn khi sử dụng Solaris sed.
Steve

56

Lưu ý rằng trên OS X, bạn có thể gặp các lỗi lạ như "mã lệnh không hợp lệ" hoặc các lỗi lạ khác khi chạy lệnh này. Để khắc phục sự cố này, hãy thử

sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>

Điều này là do trên phiên bản OSX sed, -itùy chọn mong đợi một extensionđối số để lệnh của bạn thực sự được phân tích cú pháp làm extensionđối số và đường dẫn tệp được hiểu là mã lệnh. Nguồn: https://stackoverflow.com/a/19457213


1
Đây là một điều kỳ lạ khác của OS X. May mắn thay, brew install gnu-sedcùng với PATHý chí cho phép bạn sử dụng phiên bản GNU của sed. Cảm ơn đã đề cập; một đầu cào xác định.
Doug

23

Sau đây hoạt động tốt trên máy Mac của tôi

sed -i.bak 's/foo/bar/g' sample

Chúng tôi đang thay thế foo bằng thanh trong tập tin mẫu. Sao lưu tệp gốc sẽ được lưu trong sample.bak

Để chỉnh sửa nội tuyến mà không cần sao lưu, hãy sử dụng lệnh sau

sed -i'' 's/foo/bar/g' sample

Bất kỳ cách nào để ngăn chặn việc giữ các tập tin sao lưu?
Petr Peller

@PetrPeller, Bạn có thể sử dụng lệnh được đề cập cho cùng một mẫu ... sed -i '' 's / foo / bar / g'
minhas23

18

Một điều cần lưu ý, sedkhông thể tự mình viết các tệp vì mục đích duy nhất của sed là hoạt động như một trình soạn thảo trên "luồng" (tức là các đường ống của stdin, stdout, stderr và các >&nbộ đệm, ổ cắm khác và tương tự). Với suy nghĩ này, bạn có thể sử dụng một lệnh khác teeđể ghi đầu ra trở lại tệp. Một lựa chọn khác là tạo một bản vá từ đường ống nội dung vào diff.

Phương pháp tee

sed '/regex/' <file> | tee <file>

Phương pháp vá

sed '/regex/' <file> | diff -p <file> /dev/stdin | patch

CẬP NHẬT:

Ngoài ra, lưu ý rằng bản vá sẽ khiến tệp thay đổi từ dòng 1 của đầu ra khác:

Bản vá không cần biết tệp nào sẽ truy cập vì nó được tìm thấy trong dòng đầu tiên của đầu ra từ diff:

$ echo foobar | tee fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin
*** fubar   2014-03-15 18:06:09.000000000 -0500
--- /dev/stdin  2014-03-15 18:06:41.000000000 -0500
***************
*** 1 ****
! foobar
--- 1 ----
! fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch
patching file fubar

2
/dev/stdin 2014-03-15, đã trả lời ngày 7 tháng 1 - bạn có phải là người du hành thời gian không?
Adrian Frühwirth

Trên Windows, sử dụng msysgit, /dev/stdinkhông tồn tại, do đó bạn phải thay thế /dev/stdinbằng '-', một dấu gạch nối đơn không có dấu ngoặc kép, vì vậy các lệnh sau sẽ hoạt động: $ sed 's/oo/u/' fubar | diff -p fubar -$ sed 's/oo/u/' fubar | diff -p fubar - | patch
Jim Raden

@ AdrianFrühwirth Tôi có một hộp cảnh sát màu xanh ở đâu đó xung quanh và vì một số lý do họ gọi tôi là Bác sĩ tại nơi làm việc. Nhưng thành thật mà nói, có vẻ như đồng hồ thực sự xấu trôi trên hệ thống vào thời điểm đó.
Dwight Spencer

Những giải pháp này trông vào tôi dựa vào hành vi đệm đặc biệt. Tôi nghi ngờ đối với các tệp lớn hơn, các tệp này có thể bị hỏng vì trong khi sed đang đọc, tệp có thể bắt đầu thay đổi bên dưới bằng lệnh vá hoặc thậm chí tệ hơn bởi lệnh tee sẽ cắt bớt tệp. Trên thực tế tôi không chắc chắn nếu patchkhông sẽ cắt ngắn là tốt. Tôi nghĩ rằng việc lưu đầu ra vào một tệp khác và sau đó catchuyển vào bản gốc sẽ an toàn hơn.
akostadinov

câu trả lời tại chỗ thực sự từ @ william-pursell
akostadinov

11

Không thể làm những gì bạn muốn với sed. Ngay cả các phiên bản sedhỗ trợ -itùy chọn chỉnh sửa tệp tại chỗ thực hiện chính xác những gì bạn đã tuyên bố rõ ràng mà bạn không muốn: họ ghi vào một tệp tạm thời và sau đó đổi tên tệp. Nhưng có lẽ bạn chỉ có thể sử dụng ed. Ví dụ, để thay đổi tất cả các lần xuất hiện của foođể bartrong file file.txt, bạn có thể làm:

echo ',s/foo/bar/g; w' | tr \; '\012' | ed -s file.txt

Cú pháp tương tự sed, nhưng chắc chắn không hoàn toàn giống nhau.

Nhưng có lẽ bạn nên xem xét lý do tại sao bạn không muốn sử dụng một tệp tạm thời được đổi tên. Ngay cả khi bạn không có -ihỗ trợ sed, bạn vẫn có thể dễ dàng viết một kịch bản để thực hiện công việc cho mình. Thay vì sed -i 's/foo/bar/g' file, bạn có thể làm inline file sed 's/foo/bar/g'. Một kịch bản như vậy là tầm thường để viết. Ví dụ:

#!/bin/sh -e
IN=$1
shift
trap 'rm -f $tmp' 0
tmp=$( mktemp )
<$IN "$@" >$tmp && cat $tmp > $IN  # preserve hard links

nên là đủ cho hầu hết các sử dụng.


1
vì vậy, ví dụ, bạn sẽ đặt tên cho kịch bản này như thế nào và bạn sẽ gọi nó như thế nào?
lưỡng cư

2
Tôi sẽ gọi nó inlinevà gọi nó như mô tả ở trên:inline inputfile sed 's/foo/bar/g'
William Pursell

1
wow, edchỉ trả lời mà thực sự làm tại chỗ. Lần đầu tiên tôi cần ed. Cảm ơn bạn. Một tối ưu hóa nhỏ là sử dụng echo -e ",s/foo/bar/g\012 w"hoặc echo $',s/foo/bar/g\012 w'nơi shell cho phép nó tránh trcuộc gọi thêm .
akostadinov

2
edkhông thực sự sửa đổi tại chỗ. Từ straceđầu ra, GNU edtạo một tệp tạm thời, ghi các thay đổi vào tệp tạm thời, sau đó viết lại toàn bộ tệp gốc, giữ nguyên các liên kết cứng. Từ trussđầu ra, Solaris 11 edcũng sử dụng tệp tạm thời, nhưng đổi tên tệp tạm thời thành tên tệp gốc khi lưu bằng wlệnh, phá hủy mọi liên kết cứng.
Andrew Henle

@AndrewHenle Điều đó làm nản lòng. Các edtàu có macos hiện tại (Mojave, bất kể ý nghĩa của thuật ngữ tiếp thị) vẫn bảo tồn các liên kết cứng. YMMV
William Pursell

10

Bạn có thể sử dụng vi

vi -c '%s/foo/bar/g' my.txt -c 'wq'

9

sed hỗ trợ chỉnh sửa tại chỗ. Từ man sed:

-i[SUFFIX], --in-place[=SUFFIX]

    edit files in place (makes backup if extension supplied)

Ví dụ :

Giả sử bạn có một tệp hello.txtcó văn bản:

hello world!

Nếu bạn muốn giữ một bản sao lưu của tập tin cũ, hãy sử dụng:

sed -i.bak 's/hello/bonjour' hello.txt

Bạn sẽ kết thúc với hai tệp: hello.txtvới nội dung:

bonjour world!

hello.txt.bakvới nội dung cũ.

Nếu bạn không muốn giữ một bản sao, chỉ cần không vượt qua tham số mở rộng.


3
OP đặc biệt đề cập rằng nền tảng của anh ấy không hỗ trợ tùy chọn không chuẩn (phổ biến nhưng) này.
tripleee

3

Bạn đã không chỉ định shell nào bạn đang sử dụng, nhưng với zsh, bạn có thể sử dụng =( )cấu trúc để đạt được điều này. Một cái gì đó dọc theo dòng:

cp =(sed ... file; sync) file

=( )tương tự >( )nhưng tạo một tệp tạm thời sẽ tự động bị xóa khi cpchấm dứt.


3
mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt

Nên bảo toàn tất cả các liên kết cứng, vì đầu ra được chuyển trở lại để ghi đè lên nội dung của tệp gốc và tránh mọi nhu cầu về phiên bản đặc biệt của sed.


3

Nếu bạn đang thay thế cùng một số lượng ký tự và sau khi đọc kỹ, hãy chỉnh sửa các tập tin tại chỗ của ...

Bạn cũng có thể sử dụng toán tử chuyển hướng <>để mở tệp để đọc và ghi:

sed 's/foo/bar/g' file 1<> file

Xem trực tiếp:

$ cat file
hello
i am here                           # see "here"
$ sed 's/here/away/' file 1<> file  # Run the `sed` command
$ cat file
hello
i am away                           # this line is changed now

Từ tài liệu tham khảo Bash → 3.6.10 Mở mô tả tệp để đọc và viết :

Toán tử chuyển hướng

[n]<>word

làm cho tệp có tên là mở rộng từ được mở cho cả đọc và ghi trên mô tả tệp n hoặc trên mô tả tệp 0 nếu n không được chỉ định. Nếu tập tin không tồn tại, nó được tạo ra.


Điều này bị hỏng bởi tập tin. Tôi không chắc chắn về lý do đằng sau nó. paste.fedoraproject.org/462017
Nehal J Wani

Xem các dòng [43-44], [88-89], [135-136]
Nehal J Wani

2
Blogpost này giải thích tại sao câu trả lời này không nên được sử dụng. backreference.org/2011/01/29/in-place-editing-of-files
Nehal J Wani

@NehalJWani Tôi sẽ có một bài đọc dài cho bài viết, cảm ơn vì đã ngẩng cao đầu! Điều tôi không đề cập đến trong câu trả lời là nó hoạt động nếu nó thay đổi cùng số lượng ký tự.
fedorqui 'SO ngừng làm hại'

@NehalJWani điều này đang được nói, tôi xin lỗi nếu câu trả lời của tôi gây hại cho các tập tin của bạn. Tôi sẽ cập nhật nó với các sửa chữa (hoặc xóa nó nếu cần thiết) sau khi đọc các liên kết bạn cung cấp.
fedorqui 'SO ngừng làm hại'

2

Giống như Moneypenny đã nói trong Skyfall: "Đôi khi những cách cũ là tốt nhất". Kincade nói điều gì đó tương tự sau này.

$ printf ',s/false/true/g\nw\n' | ed {YourFileHere}

Chúc mừng chỉnh sửa tại chỗ. Đã thêm '\ nw \ n' để ghi tệp. Xin lỗi vì yêu cầu trả lời chậm trễ.


Bạn có thể giải thích điều này không?
Matt Montag

1
Nó gọi ed (1), trình soạn thảo văn bản với một số ký tự được viết thành stdin. Là một người dưới 30 tuổi, tôi nghĩ rằng tôi ổn khi nói rằng ed (1) là với khủng long như khủng long đối với chúng ta. Dù sao, thay vì mở ed và gõ nội dung đó vào, jlettvin đang dẫn các nhân vật sử dụng |. @MattMontag
Ari Sweedler

Nếu bạn đang hỏi các lệnh làm gì, có hai trong số chúng, được chấm dứt bởi a \n. [phạm vi] s / <old> / <new> / <flag> là hình thức của lệnh thay thế - một mô hình vẫn được thấy trong nhiều trình soạn thảo văn bản khác (okay, vim, hậu duệ trực tiếp của ed) Dù sao, gcờ là viết tắt của "Toàn cầu" và có nghĩa là "Mọi trường hợp trên mỗi dòng mà bạn nhìn vào". Phạm vi 3,5nhìn vào dòng 3-5. ,5nhìn vào StartOfFile-5, 3,nhìn vào dòng 3-EndOfFile và ,nhìn vào StartOfFile-EndOfFile. Một lệnh thay thế toàn cầu trên mỗi dòng thay thế "false" bằng "true". Sau đó, lệnh ghi w, được nhập.
Ari Sweedler

1

Ví dụ rất tốt. Tôi đã có thách thức để chỉnh sửa tại chỗ nhiều tệp và tùy chọn -i dường như là giải pháp hợp lý duy nhất sử dụng nó trong lệnh find. Ở đây tập lệnh để thêm "phiên bản:" phía trước dòng đầu tiên của mỗi tệp:

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;
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.