Có cách nào để tạo liên kết perl -i không clobber không?


12

Một người bạn của tôi chỉ ra rằng nếu bạn làm:

perl -pi.bak -e 's/foo/bar/' somefile

khi "somefile" thực sự là một symlink, perl thực hiện đúng những gì các tài liệu nói nó sẽ làm:

Nó thực hiện điều này bằng cách đổi tên tệp đầu vào, mở tệp đầu ra theo tên gốc và chọn tệp đầu ra đó làm mặc định cho các câu lệnh print (). Tiện ích mở rộng, nếu được cung cấp, được sử dụng để sửa đổi tên của tệp cũ để tạo bản sao lưu [...]

Điều này dẫn đến một symlink mới "somefile.bak" chỉ đến tệp thực không thay đổi và một tệp thông thường mới, đã thay đổi "somefile" với các thay đổi.

Trong nhiều trường hợp, theo liên kết tượng trưng sẽ là hành vi mong muốn (ngay cả khi nó để lại vị trí chính xác của tệp .bak mơ hồ). Có cách nào đơn giản để làm việc này ngoài việc kiểm tra các liên kết tượng trưng trong trình bao bọc và xử lý trường hợp một cách thích hợp không?

( sedlàm điều tương tự, cho những gì đáng giá.)


1
Gọi vim hoặc emacs (tôi nghĩ cả hai đều theo symlink)? Nghiêm túc mà nói, tôi sợ câu trả lời là thực hiện lại -p -itrong kịch bản của bạn.
Gilles 'SO- ngừng trở nên xấu xa'

Trên thực tế, Sed không làm điều tương tự, ít nhất là kể từ phiên bản 4.2.1, được phát hành vào tháng 6 năm 2009. Bạn phải bao gồm tùy chọn --follow-symlinks để chỉnh sửa để chỉnh sửa tệp được liên kết thay vì ghi đè lên liên kết tượng trưng ; Tôi cho rằng điều này đã được thực hiện để tránh phá vỡ các tập lệnh hiện có có thể phụ thuộc vào hành vi cũ.
Garrett

Câu trả lời:


6

Tôi tự hỏi liệu spongetiện ích đa năng nhỏ ("ngâm đầu vào tiêu chuẩn và ghi vào tệp") từ moreutils sẽ hữu ích trong trường hợp này và liệu nó có theo liên kết tượng trưng không.

Tác giả mô tả sponge như thế này:

Nó giải quyết vấn đề chỉnh sửa tệp tại chỗ bằng các công cụ Unix, cụ thể là nếu bạn chỉ chuyển hướng đầu ra sang tệp bạn đang cố chỉnh sửa thì chuyển hướng có hiệu lực (ghi đè nội dung của tệp) trước lệnh đầu tiên trong đường ống được vòng để đọc từ các tập tin. Chuyển đổi thích sed -iperl -ilàm việc xung quanh điều này, nhưng không phải mọi lệnh bạn có thể muốn sử dụng trong một đường ống đều có tùy chọn như vậy và dù sao bạn cũng không thể sử dụng phương pháp đó với các đường ống nhiều lệnh.

Tôi thường sử dụng bọt biển một chút như thế này:

sed '...' file | grep '...' | sponge file

Hey tuyệt, điều đó làm việc. Tôi nghi ngờ nhận xét của Gilles ở trên là câu trả lời "thực sự", nhưng vì anh ta đã không làm nó như một câu trả lời, và vì tôi đã học được một tiện ích mới, tôi sẽ lấy cái này. :)
mattdm

Nhưng bạn đã thử nghiệm spongecho việc sử dụng như vậy trong thực tế? Còn tôi: chưa. Bạn có thể vui lòng để lại một bình luận cho biết liệu nó đã hành xử trong một bài kiểm tra theo cách muốn ở đây? Ồ, tôi thấy bình luận. Cám ơn vì đã xác nhận!
imz - Ivan Zakharyaschev

- vâng, tôi đã thử nó trước khi trả lời. Khi filetrong ví dụ của bạn ở trên là một liên kết tượng trưng, ​​liên kết được để lại một mình và tập tin thực sự đã thay đổi.
mattdm

@mattdm: Vâng, cảm ơn vì đã xác nhận! (Tôi nhận thấy những lời của bạn "làm việc" một chút chậm nhất là tôi đã viết nhận xét của tôi.)
imz - Ivan Zakharyaschev

3

Nếu bạn biết thực tế đó somefilelà một liên kết tượng trưng, ​​bạn có thể cung cấp rõ ràng đường dẫn đầy đủ đến tệp với thông tin sau:

perl -pi.bak -e 's/foo/bar/' $(readlink somefile)

Bằng cách đó, symlink gốc vẫn còn nguyên do việc thay thế hiện được thực hiện trực tiếp với tệp gốc.


Nhưng đầu ra của readlinkvẫn có thể là một liên kết tượng trưng khác. Tốt nhất là nên sử dụng -fhoặc -enếu có hoặc zsh's $file:Phoặc (:P)glob vòng như thể hiện bởi @ikegami để có được một con đường liên kết tượng trưng-miễn phí.
Stéphane Chazelas

3

Nếu bạn đang xử lý một tệp duy nhất, lệnh sẽ như sau:

perl -i -pe'...' -- "$qfn"

Giải pháp sau đó là:

perl -i -pe'...' -- "$( readlink -e -- "$qfn" )"

Điều này xử lý cả symlink và không symlink.


Nếu bạn đang xử lý một số lượng lớn tệp tùy ý, lệnh sẽ có dạng như sau:

... | xargs -r perl -i -pe'...' --

Giải pháp sau đó là:

... | xargs -r readlink -ze -- | xargs -r0 perl -i -pe'...' --

Điều này xử lý cả symlink và không symlink.


Hãy cẩn thận readlink không phải là một lệnh tiêu chuẩn, do đó, sự hiện diện, đối số và chức năng của nó thay đổi theo hệ thống, vì vậy hãy chắc chắn kiểm tra tài liệu của bạn. Ví dụ, một số triển khai có -f(mà bạn cũng có thể sử dụng ở đây), nhưng không -e, một số thì không. busyboxHành readlink -fxử như GNU readlink -e. Và một số hệ thống có một realpathlệnh thay vì readlinkthường hoạt động như GNU readlink -f.

Xin lưu ý rằng với GNU readlink -e, các liên kết tượng trưng không thể giải quyết thành tệp hiện có sẽ bị xóa một cách âm thầm.


@ Stéphane Chazelas, $var:Pkhông làm việc cho tôi. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'đầu ra tmp:P)
ikegami

bạn cần zsh 5.3 (2016) trở lên $var:P. Với các phiên bản cũ hơn, bạn luôn có thể sử dụng $var:Athay thế. Xem hướng dẫn về sự khác biệt và zsh.org/mla/users/2016/msg00593.html để biết lý do giới thiệu :P( realpath()giao diện thực ).
Stéphane Chazelas

( :Ađã được thêm vào 4.3.10 (2009), GNU readlink -zvào năm 2012, tôi không biết về bất kỳ readlinktriển khai nào khác hỗ trợ -z). Lưu ý rằng với GNU xargs, bạn thường muốn -rtùy chọn trừ khi bạn có thể đảm bảo đầu vào sẽ không trống. Ở đây, không có vấn đề gì lớn (trừ khi perlmã chứa câu lệnh BEGIN/ END), bạn sẽ nhận được -i used with no filenames on the command line, reading from STDIN.cảnh báo nếu điều đó xảy ra.
Stéphane Chazelas

Rất tiếc, tôi đã đọc sai -rtài liệu. Tôi nghĩ rằng tôi đã làm ngược lại với những gì nó làm. Hồi hộp.
ikegami
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.