Tôi có thể sử dụng `sed` để dịch các ký tự như với` tr` không?


14

Tôi muốn thay thế một bộ ký tự bằng các ký tự tương ứng từ một bộ khác, đại loại như thế này:

original set: ots
"target" set: u.x

foobartest → fuubar.ex.

Các bản dịch / phiên âm như thế này là đặc sản của trlệnh:

$ echo 'foobartest' | tr 'ots' 'u.x'
fuubar.ex.

Thật không may tr, không hỗ trợ thay đổi tập tin tại chỗ như sedkhông.
Tôi muốn sử dụng sedvì vậy tôi không phải phát minh lại bánh xe của các tệp tạm thời.


Tự trả lời câu hỏi này vì tôi dường như không thể tìm thấy bất kỳ kết quả nào cho "ký tự dịch sed". Từ khóa ma thuật cuối cùng đã trở thành "phiên âm", nhưng tôi cho rằng nó đáng để làm cho tính năng này dễ dàng tìm thấy nhất có thể.
n.st

Một số điều cần lưu ý khi cố gắng thực hiện các giải pháp cho việc này: tr(chính xác) bỏ qua đệ quy trong các bộ thay thế: echo 'abc' | tr ab bxbxc. Một giải pháp nguyên thủy có thể làm thịt xxcnó bởi vì nó áp dụng lại bản dịch cho các ký tự đã được dịch.
n.st

Liên quan: tr analog cho ký tự unicode? (GNU sedtrái với GNU trcó thể chuyển ngữ các ký tự nhiều byte)
Stéphane Chazelas

Nếu bạn muốn một khả năng khác: perl có thể dịch, và -i, và (trừ khi cổ đại). Không phải POSIX, nhưng khá phổ biến.
dave_thndry_085

Câu trả lời:


24

sedylệnh hoạt động giống như tr:

$ echo 'foobartest' | sed 'y/ots/u.x/'
fuubar.ex.

Các ylệnh này là một phần của POSIX sedđặc điểm kỹ thuật , vì vậy nó sẽ làm việc trên chỉ là về bất kỳ nền tảng.

Và vì nó sed, bạn có thể yêu cầu nó thay thế một tệp bằng phiên bản đã chỉnh sửa của mình, cho phép bạn kinh doanh tệp tạm thời khó chịu (với điều kiện bạn triển khai sedhỗ trợ -itùy chọn, không được chỉ định bởi POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt

@ StéphaneChazelas Cảm ơn bạn đã chỉ ra điều đó; Tôi đã không nhận thức được các hoạt động bên trong cho đến bây giờ. Tôi đã chỉnh sửa câu trả lời của mình để đề cập đến điều đó.
n.st

Cảm ơn, điều này là vô cùng hữu ích! Tôi đã mong đợi nó hoạt động trong VIM (8.0.1092 trên CentOS 7.3) nhưng không được. VIM có nên làm gì không?
dotancohen

1
@dotancohen Chỉ vì chức năng thay thế của Vim được mô hình hóa sau khi sedkhông có nghĩa là các chức năng khác cũng vậy. ;) Danh sách gửi thư Vim có một chủ đề về việc tìm kiếm một y/abc/def/tương đương; lựa chọn tốt nhất có vẻ là :%call setline(".", tr(getline("."),"abc","def")).
n.st

8

Nếu giống như trong trường hợp của bạn, bạn đang chuyển ngữ các ký tự mà không thay đổi kích thước của chúng (dù sao, một số triển khai như GNU trchỉ hỗ trợ các ký tự một byte), bạn có thể làm:

tr 'ots' 'u.x' < file 1<> file

Đó là, đã trghi đè lên tệp.

Điều đó tốt hơn sed -itrên một số tài khoản:

  • nó không cần thêm dung lượng đĩa (ngoại trừ một số tệp thưa thớt, các trường hợp đặc biệt sao chép khi ghi)
  • nó bảo tồn số inode, quyền sở hữu, quyền, ACL ...
  • Nó hoạt động tốt với các liên kết tượng trưng, ​​nó không phá vỡ các liên kết cứng
  • nó không để các tập tin tạm thời nói dối khi bị giết.

Một nhược điểm là nếu nó bị gián đoạn, tập tin sẽ bị dịch một nửa (tuy nhiên trong trường hợp này, bạn có thể chạy lại để hoàn thành nó). Một số sedtriển khai sẽ xử lý chính xác bằng cách đảm bảo tệp gốc không thay đổi trừ khi lệnh thành công.


3
Hãy cẩn thận chạy lại bản dịch nếu bạn có đệ quy trong các bộ dịch, vd echo 'abc' | tr ab bx.
n.st

1
@ n.st, vâng, đó là lý do tại sao tôi nói trong trường hợp này , mặc dù tôi đồng ý rằng nó đáng để đánh vần nó.
Stéphane Chazelas

Cuối cùng, tôi đã phải làm việc với các tập tin tạm thời sau khi tất cả: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - Các nhân vật multibyte đã làm cho nó không thể sử dụng GNU trvà trong môi trường PXE liên kết tượng trưng nặng của chúng tôi, sed -ilà một chờ đợi vít-up để xảy ra sự kiện: /
n.st

@ n.st, iconv -t cp437có vẻ thích hợp hơn cho điều đó.
Stéphane Chazelas

iconvphá vỡ khi tệp đầu vào đã chứa byte được mã hóa cp437 hoặc hỗn hợp nhiều mã hóa. Vì vậy, trong trường hợp tốt hơn trong trường hợp chung, việc thay thế thủ công trong trường hợp này sẽ mạnh mẽ hơn.
n.st

4

Thay vào đó, nếu vấn đề chính của bạn là thiếu hỗ trợ thay đổi tệp tại chỗ, bạn có thể quan tâm đến spongecông cụ từ gói moreutils :

tr 'ots' 'u.x' < file | sponge file

sẽ ghi vào file, nhưng chỉ mở fileđể viết khi đầu vào hoàn tất. Từ trang hướng dẫn :

spongeđọc đầu vào tiêu chuẩn và ghi nó ra tập tin được chỉ định. Không giống như chuyển hướng shell, bọt biển thấm tất cả đầu vào của nó trước khi mở tệp đầu ra. Điều này cho phép xây dựng các đường ống đọc và ghi vào cùng một tệp.

Trừ khi bạn có các tệp thực sự lớn không thể lưu trong bộ nhớ, spongecó thể phù hợp với bạn.


2
Một vấn đề spongelà nó vẫn ghi đè filenếu trthất bại (ví dụ nếu bạn đã viết nhưng không đọc quyền truy cập file)
Stéphane Chazelas

Ồ, thực sự nó có; Tôi không mong đợi điều đó. Cảm ơn.
mindriot

Xem cat file >; filetoán tử của ksh93, ghi đầu ra vào tempfile, được đổi tên thành đích chỉ khi lệnh thành công (nhưng giống như sed -i, nó tạo ra một tệp mới thay vì ghi đè lên bản gốc).
Stéphane Chazelas
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.