Làm cách nào để thực hiện bất kỳ lệnh nào chỉnh sửa tệp (đối số) của nó “tại chỗ” bằng cách sử dụng bash?


110

Tôi có một tệp temp.txt, mà tôi muốn sắp xếp bằng sortlệnh trong bash.

Tôi muốn kết quả được sắp xếp thay thế tệp gốc.

Ví dụ: điều này không hoạt động (tôi nhận được một tệp trống):

sortx temp.txt > temp.txt

Điều này có thể được thực hiện trong một dòng mà không cần sử dụng đến việc sao chép vào các tệp tạm thời không?


CHỈNH SỬA: -oTùy chọn này rất thú vị cho sort. Tôi đã sử dụng sorttrong câu hỏi của mình như một ví dụ. Tôi gặp phải vấn đề tương tự với các lệnh khác:

uniq temp.txt > temp.txt.

Có giải pháp chung nào tốt hơn không?


Câu trả lời:


171
sort temp.txt -o temp.txt

3
Đây là một câu trả lời. Tôi đã thực sự tự hỏi nếu có một giải pháp chung cho vấn đề này. Ví dụ: nếu tôi muốn tìm tất cả các dòng UNIQ trong một tệp "tại chỗ", tôi không thể thực hiện -o
jm.

Nó không phải là chung chung, nhưng bạn có thể sử dụng -u với sắp xếp GNU để tìm các dòng duy nhất
James

Có ai đã giải quyết vấn đề để cho phép vd sort --inplace *.txt? Điều đó sẽ rất thú vị
sehe

@sehe Hãy thử điều này:find . -name \*.txt -exec sort {} -o {} \;
Keith Gaughan

29

A sortcần xem tất cả đầu vào trước khi nó có thể bắt đầu xuất. Vì lý do này, sortchương trình có thể dễ dàng cung cấp một tùy chọn để sửa đổi tệp tại chỗ:

sort temp.txt -o temp.txt

Cụ thể, tài liệu của GNUsort cho biết:

Thông thường, sắp xếp đọc tất cả đầu vào trước khi mở tệp đầu ra, vì vậy bạn có thể sắp xếp tệp tại chỗ một cách an toàn bằng cách sử dụng các lệnh như sort -o F Fcat F | sort -o F. Tuy nhiên, sortwith --merge( -m) có thể mở tệp đầu ra trước khi đọc tất cả đầu vào, vì vậy một lệnh như cat F | sort -m -o F - Gkhông an toàn vì sắp xếp có thể bắt đầu viết Ftrước khi catđọc xong.

Trong khi tài liệu của BSD sortcho biết:

Nếu [the] output-file là một trong những tập tin đầu vào, hãy sắp xếp sao chép nó vào một tập tin tạm thời trước khi sắp xếp và ghi kết quả vào [the] output-file.

Các lệnh như uniqcó thể bắt đầu ghi đầu ra trước khi đọc xong đầu vào. Các lệnh này thường không hỗ trợ chỉnh sửa tại chỗ (và chúng sẽ khó hỗ trợ tính năng này hơn).

Bạn thường giải quyết vấn đề này với một tệp tạm thời hoặc nếu bạn hoàn toàn muốn tránh có tệp trung gian, bạn có thể sử dụng bộ đệm để lưu trữ kết quả hoàn chỉnh trước khi viết ra. Ví dụ, với perl:

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

Ở đây, phần perl đọc kết quả hoàn chỉnh từ uniqtrong biến$_ và sau đó ghi đè lên tệp gốc bằng dữ liệu này. Bạn có thể làm điều tương tự bằng ngôn ngữ kịch bản mà bạn chọn, thậm chí có thể trong Bash. Nhưng lưu ý rằng nó sẽ cần đủ bộ nhớ để lưu toàn bộ tệp, điều này không được khuyến khích khi làm việc với các tệp lớn.


19

Đây là một cách tiếp cận tổng quát hơn, hoạt động với uniq, sort và whatnot.

{ rm file && uniq > file; } < file

14
Một cách tiếp cận chung chung, với spongetừ moreutils: cat file |frobnicate |sponge file.
Tobu

3
@Tobu: tại sao không gửi câu trả lời đó như một câu trả lời riêng?
Flimm,

1
Có thể tốt khi lưu ý rằng điều này không nhất thiết phải bảo toàn quyền đối với tệp. Umask của bạn quyết định các quyền mới sẽ là gì.
wor

1
Mánh khôn. Bạn có thể giải thích nó hoạt động chính xác như thế nào không?
patryk.beza

2
@ patryk.beza: Theo thứ tự: FD đầu vào được mở từ tệp gốc; mục nhập thư mục gốc bị xóa; chuyển hướng được xử lý, tạo một tệp trống mới có cùng tên với tệp cũ đã từng có; thì lệnh chạy.
Charles Duffy

10

Nhận xét của Tobu về miếng bọt biển đảm bảo là một câu trả lời theo đúng nghĩa của nó.

Để trích dẫn từ trang chủ moreutils :

Có lẽ công cụ mục đích chung nhất trong moreutils cho đến nay là bọt biển (1), cho phép bạn làm những việc như sau:

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

Tuy nhiên, spongegặp phải vấn đề tương tự Steve Jessop bình luận ở đây. Nếu bất kỳ lệnh nào trong đường dẫn trước đó bị spongelỗi, thì tệp gốc sẽ được ghi lại.

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

Uh-oh, my-important-fileđã biến mất.


1
Sponge biết rằng nó sẽ được sử dụng để thay thế tệp đầu vào và ban đầu nó tạo một tệp tạm thời để tránh tình trạng chạy đua. Để điều này hoạt động, bọt biển phải là phần tử cuối cùng trong đường ống và nó phải được phép tự tạo tệp đầu ra (ví dụ: trái ngược với chuyển hướng đầu ra cấp shell). BTW: Có vẻ như một cách khắc phục mã nguồn dễ dàng cho trường hợp 'fail' là không đổi tên tệp tạm thời trong trường hợp pipefail (không biết tại sao bọt biển không có tùy chọn đó).
Brent Bradburn

Tôi nghĩ nếu bạn thêm set -o pipefailvào đầu tập lệnh của mình, lỗi trên mistyped_command my-important-filesẽ khiến tập lệnh thoát ngay lập tức, trước khi thực thi sponge, do đó bảo toàn tệp quan trọng.
Elouan Keryell-Even

6

Đây là một dòng:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

Về mặt kỹ thuật, không có việc sao chép vào tệp tạm thời và lệnh 'mv' sẽ được thực hiện ngay lập tức.


6
Hừm. Tôi vẫn gọi temp.txt.sort là một tệp tạm thời.
JesperE

5
Mã này là rủi ro, bởi vì nếu sắp xếp không thành công vì bất kỳ lý do gì mà không hoàn thành công việc của nó, bản gốc sẽ bị ghi đè.
Steve Jessop 28/09/08

1
Thiếu dung lượng ổ đĩa là một nguyên nhân chính đáng hoặc một tín hiệu (người dùng nhấn CTRL-C).
Steve Jessop 28/09/08

5
nếu bạn muốn sử dụng một cái gì đó như thế này, hãy sử dụng && (logic và) thay vì; bởi vì sử dụng điều đó sẽ đảm bảo rằng nếu một lệnh không thành công thì lệnh tiếp theo sẽ không được thực hiện. ví dụ: cp backup.tar /root/backup.tar && rm backup.tar nếu bạn không có quyền sao chép, bạn sẽ được an toàn vì tệp sẽ không bị xóa
daniels 28/09/08

1
đã thay đổi câu trả lời của tôi để tính đến các đề xuất của bạn, cảm ơn
davr 29/09/08

4

Tôi thích sort file -o filecâu trả lời nhưng không muốn nhập cùng một tên tệp hai lần.

Sử dụng mở rộng lịch sử BASH :

$ sort file -o !#^

lấy đối số đầu tiên của dòng hiện tại khi bạn nhấn enter.

Một loại duy nhất tại chỗ:

$ sort -u -o file !#$

lấy đối số cuối cùng trong dòng hiện tại.


3

Nhiều người đã đề cập đến tùy chọn -o . Đây là phần trang người đàn ông.

Từ trang người đàn ông:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.

3

Điều này sẽ bị hạn chế về bộ nhớ, nhưng bạn có thể sử dụng awk để lưu trữ dữ liệu trung gian trong bộ nhớ, sau đó ghi dữ liệu đó ra ngoài.

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

Tôi nghĩ đó là khả năng những >sẽ cắt cụt các tập tin trước khi lệnh ( uniqtrong trường hợp này) đọc nó.
Martin

3

Một giải pháp thay thế cho spongephổ biến hơn sed:

sed -ni r<(command file) file

Nó hoạt động cho bất kỳ lệnh ( sort, uniq, tac, ...) và sử dụng rất nổi tiếng sedcủa -itùy chọn (chỉnh sửa file tại chỗ).

Cảnh báo: Hãy thử command filetrước vì việc chỉnh sửa tệp tại chỗ về bản chất không an toàn.


Giải trình

Thứ nhất, anh nói sedkhông in (bản gốc) dòng ( -ntùy chọn ), và với sự giúp đỡ của sed's rlệnhbash' s Process Thay , nội dung được tạo ra bởi <(command file)sẽ là đầu ra lưu tại chỗ .


Làm mọi thứ dễ dàng hơn

Bạn có thể gói giải pháp này thành một hàm:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

Thí dụ

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file

1

Sử dụng đối số --output=hoặc-o

Vừa thử trên FreeBSD:

sort temp.txt -otemp.txt

Mặc dù đúng, nó chỉ đơn giản là một bản sao của câu trả lời này
từ

1

Để thêm uniqkhả năng, những nhược điểm của:

sort inputfile | uniq | sort -o inputfile


0

Nếu bạn khăng khăng muốn sử dụng sortchương trình, bạn phải sử dụng một tệp trung gian - tôi không nghĩ rằng sortcó tùy chọn sắp xếp trong bộ nhớ. Bất kỳ thủ thuật nào khác với stdin / stdout sẽ không thành công trừ khi bạn có thể đảm bảo rằng kích thước bộ đệm cho stdin của sắp xếp đủ lớn để phù hợp với toàn bộ tệp.

Chỉnh sửa: xấu hổ về tôi. sort temp.txt -o temp.txthoạt động xuất sắc.


Tôi đọc Q cũng là "tại chỗ" nhưng lần đọc thứ hai khiến tôi tin rằng anh ấy không thực sự yêu cầu nó
epatel 28/09/08

0

Giải pháp khác:

uniq file 1<> file

Cần lưu ý rằng <>thủ thuật chỉ hoạt động trong trường hợp này vì uniqnó đặc biệt ở chỗ nó chỉ sao chép các dòng đầu vào sang các dòng đầu ra, bỏ một số trên đường đi. Nếu lệnh khác (ví dụ sed) được sử dụng để thay đổi đầu vào (ví dụ: sẽ thay đổi mọi athành aa), thì nó có thể ghi đè filetheo những cách không có ý nghĩa và thậm chí lặp vô hạn, miễn là đầu vào đủ lớn (hơn một bộ đệm đọc đơn).
David
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.