Perilaku aneh tr menggunakan rentang

10

Saya memiliki satu server tertentu yang menunjukkan perilaku aneh saat menggunakan tr. Berikut adalah contoh dari server yang berfungsi:

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

Itu masuk akal bagi saya.

Namun, ini dari server 'khusus':

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

Seperti yang Anda lihat, menghapus semua karakter huruf kecil gagal. TAPI, itu telah menghapus huruf 'o'

Bagian yang menarik adalah dua contoh berikut, yang sama sekali tidak masuk akal bagi saya:

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

(lagi, 'o' dihapus pada contoh terakhir)

Adakah yang tahu apa yang sedang terjadi di sini? Saya tidak dapat mereproduksi pada kotak linux lain yang saya gunakan.

Chris
sumber
5
Terkait secara tangensial: trrentang ditulis tanpa melampirkan [...]. Jadi tr -d '[a-z]'akan membunuh a-z, dan juga karakter [dan ]. Gunakan tr -d a-zuntuk membunuh hanya surat a-z.
Satō Katsura

Jawaban:

24

Anda memiliki file bernama odi direktori saat ini

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

shell akan memperluas [a-z]string jika kecocokan ditemukan.

Ini disebut ekspansi pathname, menurut man bash

Perluasan Pathname
Setelah pemisahan kata, kecuali opsi -f telah ditetapkan, bash memindai setiap kata untuk karakter *,?, Dan [. ... (...)

bash akan melakukan ekspansi.

[...] Cocok dengan salah satu karakter yang terlampir.

Archemar
sumber
@ Chris Anda dapat memeriksa ekspansi shell menggunakan misalnya echo: touch o ; echo tr -d [a-z]memberikan ini:tr -d o
pabouk
8

Apa yang terjadi

Shell (bash) melihat argumennya [a-z]. Itu adalah pola wildcard ( gabus ), yang cocok dengan huruf kecil apa pun¹. Karenanya shell mencari nama file yang cocok dengan pola ini. Ada tiga kasus:

  • Tidak ada file di direktori saat ini memiliki nama yang huruf kecil. Kemudian shell meninggalkan pola wildcard tidak berubah, dan trmelihat argumen -ddan [a-z]. Inilah yang terjadi pada sebagian besar mesin Anda.
  • Satu file dalam direktori saat ini memiliki nama yang merupakan huruf kecil. Kemudian shell memperluas pola ke nama file ini, dan trmelihat argumen -ddan nama file. Ini terjadi di server, dan file yang cocok dipanggil okarena kita dapat melihat bahwa trmenghapus surat itu o.
  • Dua atau lebih file dalam direktori saat ini memiliki nama yang huruf kecil. Kemudian shell memperluas pola ke daftar nama file yang cocok, dan trmelihat tiga argumen atau lebih: -ddan nama file. Karena trmengharapkan satu argumen setelahnya -d, itu akan mengeluh.

Apa yang seharusnya Anda lakukan

Jika ada karakter khusus dalam argumen perintah, Anda harus menghindarinya. Letakkan argumen dalam tanda kutip tunggal '…'(ini adalah cara paling sederhana, ada yang lain). Di dalam kutipan tunggal, semua karakter berdiri sendiri kecuali kutipan tunggal itu sendiri. Jika ada satu kutipan di dalam argumen, gantilah dengan'\'' .

tr -d '[a-z]'

Namun perhatikan bahwa ini mungkin masih bukan yang Anda maksudkan! Ini memberitahu Anda truntuk menghapus huruf kecil dan tanda kurung. Ini setara dengan tr -d ']a-z[', tr '[]a-z', dll Untuk menghapus huruf kecil, gunakan

tr -d a-z

Argumen untuk tradalah serangkaian karakter. Anda menempatkan tanda kurung di sekitar set karakter dalam ekspresi reguler atau pola wildcard untuk menunjukkan bahwa itu adalah set karakter. Tetapi trbekerja pada satu karakter pada satu waktu. Argumen baris perintahnya adalah apa yang Anda masukkan ke dalam tanda kurung .

Anda memang membutuhkan tanda kurung untuk menunjukkan kelas karakter . Dalam ekspresi reguler, Anda menggunakan tanda kurung di dalam tanda kurung untuk menunjukkan kelas karakter, misalnya [[:lower:]]*cocok dengan sejumlah huruf kecil, [[:lower:]_]*cocok dengan sejumlah huruf kecil dan garis bawah. Dalam argumen tr, Anda memerlukan set tanpa tanda kurung di sekitarnya, jadi tr -d '[:lower:]'hapus huruf kecil, tr -d '[:lower:]_'hapus huruf kecil dan garis bawah, dll.

¹ Di beberapa tempat mungkin cocok dengan karakter lain .

Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
Perhatikan bahwa pada Solaris 10 (dan lainnya beragam Unix SysV kuno berdasarkan), Anda perlu tr -d '[a-z]'dengan /usr/bin/tr. Dengan /usr/xpg4/bin/tr, tr -d a-zberfungsi tetapi tr -d '[a-z]'tidak menghapus [atau ].
Stéphane Chazelas
1
/usr/xpg4/bin/tr -d '[a-z]'tidak menghapus [atau ]tampaknya diperbaiki di Solaris 11.
Stéphane Chazelas