Hành vi kỳ lạ của tr sử dụng phạm vi


10

Tôi có một máy chủ cụ thể đang thể hiện hành vi lạ khi sử dụng tr. Đây là một ví dụ từ một máy chủ hoạt động:

-bash-3.2$ echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
-bash-3.2$

Điều đó làm cho ý nghĩa hoàn hảo với tôi.

Tuy nhiên, đây là từ máy chủ 'đặc biệt':

[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890

Như bạn có thể thấy, xóa tất cả các ký tự chữ thường đều thất bại. NHƯNG, nó đã xóa chữ 'o'

Phần thú vị là hai ví dụ sau đây, không có ý nghĩa gì với tôi:

[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-n]
opqrstuvwxyz1234567890
[root@host~]# echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-o]
abcdefghijklmnpqrstuvwxyz1234567890
[root@host~]#

(một lần nữa, 'o' bị xóa trong ví dụ trước)

Có ai có bất cứ ý tưởng những gì đang xảy ra ở đây? Tôi không thể sao chép trên bất kỳ hộp linux nào khác mà tôi đang sử dụng.


5
Liên quan liên tục: trphạm vi được viết mà không kèm theo [...]. Vì vậy, tr -d '[a-z]'sẽ giết a-z, và nhân vật []. Sử dụng tr -d a-zđể chỉ giết các chữ cái a-z.
Satō Katsura

Câu trả lời:


24

bạn có một tập tin có tên otrong thư mục hiện tại

foo> ls
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
1234567890
foo> touch o
foo> echo "abcdefghijklmnopqrstuvwxyz1234567890"|tr -d [a-z]
abcdefghijklmnpqrstuvwxyz1234567890

Shell sẽ mở rộng [a-z]chuỗi nếu tìm thấy kết quả khớp.

Đây được gọi là mở rộng tên đường dẫn, theo man bash

Mở rộng tên đường dẫn
Sau khi tách từ, trừ khi tùy chọn -f đã được đặt, bash quét từng từ cho các ký tự *,?, Và [. ... (...)

bash sẽ thực hiện mở rộng.

[...] Khớp với bất kỳ một trong các ký tự kèm theo.


@Chris Bạn có thể kiểm tra bản mở rộng của shell bằng cách sử dụng ví dụ echo: touch o ; echo tr -d [a-z]đưa ra điều này:tr -d o
pabouk

8

Chuyện gì đang xảy ra

Shell (bash) thấy đối số [a-z]. Đó là mẫu ký tự đại diện (hình cầu ), khớp với bất kỳ chữ cái viết thường nào. Do đó, shell tìm kiếm một tên tệp phù hợp với mẫu này. Có ba trường hợp:

  • Không có tệp nào trong thư mục hiện tại có tên là một chữ cái viết thường. Sau đó, vỏ để lại mẫu ký tự đại diện không thay đổi, và trthấy các đối số -d[a-z]. Đây là những gì xảy ra trên hầu hết các máy của bạn.
  • Một tập tin trong thư mục hiện tại có một tên là một chữ cái viết thường. Sau đó, shell mở rộng mẫu cho tên tệp này và trxem các đối số -dvà tên tệp. Điều này xảy ra trên máy chủ và tệp phù hợp được gọi ovì chúng ta có thể thấy rằng trđã xóa chữ cái o.
  • Hai hoặc nhiều tệp trong thư mục hiện tại có một tên là một chữ cái viết thường. Sau đó, shell mở rộng mẫu vào danh sách các tên tệp phù hợp và trthấy ba hoặc nhiều đối số: -dvà tên tệp. Vì trmong đợi một cuộc tranh cãi duy nhất sau -dđó, nó sẽ phàn nàn.

Những gì bạn nên làm

Nếu có các ký tự đặc biệt trong đối số của lệnh, bạn phải thoát chúng. Đặt đối số trong dấu ngoặc đơn '…'(đây là cách đơn giản nhất, có những cách khác). Bên trong các trích dẫn đơn, tất cả các ký tự đứng cho chính họ ngoại trừ chính trích dẫn đó. Nếu có một trích dẫn bên trong đối số, thay thế nó bằng'\'' .

tr -d '[a-z]'

Tuy nhiên lưu ý rằng đây có lẽ vẫn không phải là ý bạn! Điều này nói trđể xóa các chữ cái viết thường và dấu ngoặc vuông. Nó tương đương với tr -d ']a-z[', tr '[]a-z'v.v. Để xóa các chữ cái thường, hãy sử dụng

tr -d a-z

Đối số trlà một bộ ký tự. Bạn đặt dấu ngoặc quanh một bộ ký tự trong một biểu thức thông thường hoặc mẫu ký tự đại diện để chỉ ra rằng đó là một bộ ký tự. Nhưng trhoạt động trên một nhân vật tại một thời điểm. Đối số dòng lệnh của nó là những gì bạn đặt trong dấu ngoặc .

Bạn cần dấu ngoặc để chỉ các lớp ký tự . Trong một biểu thức thông thường, bạn sử dụng dấu ngoặc trong ngoặc để biểu thị một lớp ký tự, ví dụ: [[:lower:]]*khớp với bất kỳ số chữ cái viết thường nào, [[:lower:]_]*khớp với bất kỳ số lượng chữ cái viết thường và dấu gạch dưới. Trong đối số của tr, bạn cần tập hợp không có dấu ngoặc xung quanh, vì vậy tr -d '[:lower:]'xóa các chữ cái viết thường, tr -d '[:lower:]_'xóa các chữ cái thường và dấu gạch dưới, v.v.

¹ Trong một số miền địa phương nó có thể phù hợp với các nhân vật khác .


1
Lưu ý rằng trên Solaris 10 (và các Unice dựa trên SysV cổ đại khác), bạn cần tr -d '[a-z]'/usr/bin/tr. Với /usr/xpg4/bin/tr, tr -d a-zhoạt động nhưng tr -d '[a-z]'không xóa [cũng không ].
Stéphane Chazelas

1
/usr/xpg4/bin/tr -d '[a-z]'không xóa [cũng không ]được sửa trong Solaris 11.
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.