Bulun (1): Bazı dosya adlarında başarısız olması için uygulanan yıldız joker karakter nasıl?


31

Dosya adlarının UTF-8’de olduğu bir dosya sisteminde, hatalı bir adla bir dosyam var; şöyle gösterilir:, D�sinstallerzsh: 'e göre gerçek ad D$'\351'sinstaller, Latin1 Désinstaller, kendisi için "kaldır" için bir Fransız barbarlığı. Zsh, onunla eşleşmeyecek, [[ $file =~ '^.*$' ]]ancak bir *dünya ile eşleşecekti - beklediğim davranış bu.

Şimdi hala çalışırken bulmayı bekliyorum find . -name '*'. Aslına bakarsanız, bir dosya adının bu testte başarısız olmasını asla beklemem. Ancak, ile LANG=en_US.utf8, dosya yok değil göstermek ve ben setine sahip LANG=C(ya da en_US, ya da ''işe bunun için).

Soru: Arkasındaki uygulama nedir ve bu sonucu nasıl tahmin edebilirdim?

Bilgiler: Arch Linux 3.14.37-1-lts, bul (GNU findutils) 4.4.2


1
convmvdosya adlarını utf-8'e dönüştürmeyi düşündünüz mü?
ctrl-alt-delor

@richard: Aslında, dosya ismini [[ $file =~ '^.*$' ]]kullanmakta başarısız olduğuma güvenmek için kullanıyorum recode, fakat şimdi convmvgerekip gerekmediğine bakacağım . Teşekkürler.
Michaël

Yanıtlar:


25

Bu gerçekten hoş bir av. GNU bulma kaynak koduna hızlıca baktığımda, bunun fnmatchgeçersiz bayt sekanslarında ( pred_name_commonin pred.c) nasıl davrandığına bağlı olduğunu söyleyebilirim :

b = fnmatch (str, base, flags) == 0;
(...)
return b;

Bu kod fnmatch0 ile eşitlik için dönüş değerini test eder , ancak hataları kontrol etmez; bu, "eşleşmiyor" olarak bildirilen hatalarla sonuçlanır.

Her zaman true dönmek için bu libc işlevin davranışını değiştirmek için, yıllar önce, öne sürülmüştür *bile kırık dosya adları, desen, ama fikir söyleyebilirim gelen başlayan iplik (bkz reddedilmiş olmalıdır https : //sourceware.org/ml/libc-hacker/2002-11/msg00071.html ):

Fnmatch geçersiz bir çok baytlık karakter tespit ettiğinde, tek bayt eşleşmesine geri dönmelidir, böylece "*" böyle bir dizeyle eşleşme şansına sahip olur.

Ve bu neden daha iyi veya daha doğru? Mevcut bir uygulama var mı?

Stéphane Chazelas tarafından bir yorumda ve aynı 2002 başlığında belirtildiği gibi, bu, geçersiz karakterlerde boğulmayan mermilerin yaptığı glob genişlemesiyle tutarsızdır. Belki de daha da şaşırtıcı olan, testi tersine çevirmenin yalnızca bozuk adlara sahip dosyalarla eşleşmesidir (bash ile dosyalar oluşturun touch $'D\351marrer' $'Touch\303\251' $'\346\227\245\346\234\254\350\252\236'):

$ find -name '*'
.
./Touché
./日本語

$ find -not -name '*'
./D?marrer

Dolayısıyla sorunuzu yanıtlamak için, fnmatchbu durumda sizin davranışınızı ve findbu fonksiyonun geri dönüş değerini nasıl idare edeceğini bilerek bunu önceden tahmin etmiş olabilirsiniz ; Muhtemelen yalnızca belgeleri okuyarak öğrenemezsiniz.


Neden düzeltilmediğine dair tahminim *, o zaman bunun ile tutarsız olacağı yönünde D*staller.
ctrl-alt-delor

7
@richard fikir olurdu D*stallerüzerinde eşleşir $'D\351sinstaller'o Ive'sınav tüm kabukların topak yaptığı gibi de. GNU uyumsuz davranışının GNU kabuğuyla tutarlı olmadığı göz önüne alındığında bunun bir hata olduğunu söyleyebilirim.
Stéphane Chazelas

1
Büyük eksiklik cevabı, dhag; çok takdir etmek. Fnmatch'in hangi standartlara uygun olduğuna dikkat eder misiniz? Her zamanki POSIX regexp'in .kodlamada yalnızca geçerli karakterlerle eşleşmesi gerektiğini belirten spesifikasyonlar bulabilirim - bu nedenle .*geçersiz dizelerle eşleşmeyen beklentim - fakat genel yıldız için eşleşen bir özellik bulamıyorum.
Michaël

1
Çevrimiçi bulabileceğim en yakın özellik bu OpenGroup sayfasında . Eşleşmenin, karakterin grafik gösterimini değil, karakteri kodlamak için kullanılan bit desenine dayanması gerektiğini belirtir . ve <asterisk>, boş dize de dahil olmak üzere herhangi bir dizeyle eşleşecek bir kalıptır. Bu tartışmaya göre @ StéphaneChazelas'ın önerisi olarak yorumlanabilir. 13 yıl sonra, tekrar yukarı ping yapma zamanı olabilir :-)
Michaël

@ Michaël, daha iyisini de bulamadım. Belki de, bir karşılaştırma noktası olarak GNU, Mac OS'de bulduğu kabuğun globbingiyle tutarlı bir şekilde davranır (yani, -name '*'tüm dosyaları, kırık isimleri dahil eder), bu nedenle, muhtemelen fnmatchBSD'nin POSIX.2’nin önemini iddia etmeyen versiyonunu , GNU versiyonundan farklı olarak, geçersiz karakterlerde yapılması gerekenlerin yorumlanması farklı ve tartışmalı olarak daha açıktır.
dhag,

13

find -name seçeneği, eşleşen dosya adını gerçekleştirmek için kabuk deseni eşleme gösterimini kullanır . Birden fazla karakterle eşleşen* bir kalıptır , sıfır veya daha fazla karakterden oluşan bir dizeyle eşleşir.

finddesen eşleşmesini kontrol etmek için fnmatch işlevini kullanır , böylece sonucu kontrol etmek için ltrace kullanabilirsiniz :

$ touch $'\U1212'aa
$ touch D$'\351'sinstaller
$ LC_ALL=en_US.utf8 ltrace -e fnmatch find -name '*'          
find->fnmatch("foo", "foo", 0)                   = 0
find->fnmatch("Foo", "foo", 0)                   = 1
find->fnmatch("Foo", "foo", 16)                  = 0
find->fnmatch("*", ".", 0)                       = 0
.
find->fnmatch("*", "D\351sinstaller", 0)         = -1
find->fnmatch("*", "\341\210\222aa", 0)          = 0
./ሒaa
+++ exited (status 0) +++

İle D\351sinstaller, fnmatchdönüş -1, eşleşmediğini belirtti. Gibi geçerli bir karakter ሒaaeşleşecek.

Senin durumunda olan UTF-8yerel ayar, \351desen eşleştirme başarısız neden geçersiz karakter.


3
En azından, kullanımı için +1 ltrace. Bunu biliyordum straceama ltracebenim için yeni. Güzel!
Michaël
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.