Bash'de boş baytları nasıl kullanırım?


33

Bash'deki dosya yolları boş bayt (sıfır değerli bayt $'\0') dışında herhangi bir karakter içerebildiğinden , boş baytı ayırıcı olarak kullanmanın en iyisi olduğunu okudum . Örneğin, çıktısı findbaşka bir programa gönderilecekse, -print0seçeneğin kullanılması önerilir ( findbunun sürümleri için).

Ancak bunun gibi bir şey iyi çalışsa da (yeni satırlarla ayrılmış dosya yollarını yazdırmak - endişelenmeyin, bu sadece bir gösteri, aslında bunu gerçek komut dosyalarında yapmıyorum):

find -print0 \
  | while IFS= read -r -d $'\0' ; do echo "$REPLY" ; done

Böyle bir şey yok değil çalışır:

for file in * ; do echo -n "$file"$'\0' ; done \
  | while IFS= read -r -d $'\0' ; do echo "$REPLY" ; done

Sadece for-loop bölümünü denediğimde , aralarındaki boş bayt olmadan tüm dosya adlarını birlikte yazdırdığını buldum .

Bu neden? Neler oluyor?

Yanıtlar:


43

Bash, dahili olarak boş baytlarla sonlandırılan C tarzı dizeleri kullanır. Bu, bir Bash dizesinin (örneğin bir değişkenin değeri veya bir komutun argümanı gibi) aslında boş bir bayt içeremeyeceği anlamına gelir. Örneğin, bu mini script:

foobar=$'foo\0bar'    # foobar='foo' + null byte + 'bar'
echo "${#foobar}"     # print length of $foobar

aslında yazdırır 3, çünkü $foobaraslında sadece 'foo': bardizgenin sonundan sonra gelir.

Benzer şekilde, echo $'foo\0bar'sadece baskı yapar foo, çünkü kısmı echobilmez \0bar.

Gördüğünüz gibi, \0sıra bir $'...'stil dizesinde aslında çok yanıltıcıdır ; dize içinde boş bir bayt gibi görünüyor, ancak bu şekilde çalışma sonunda bitmiyor. İlk örneğinizde reademrinizde var -d $'\0'. Bu işe yarıyor, fakat -d ''aynı zamanda işe yarıyor! (Yani bir açıkça belgelenmiş bir özellik değil read, ama aynı nedenle çalışır varsayalım: ''Boş dize, sonlandırıcı boş bayt hemen gelir böylece. "Nin ilk karakteri kullanılarak belgelenmiştir delim ", ben bile eserlerini tahmin "ilk karakter" dizenin sonundan sonraysa!)-d delim

Eğer bildiğimiz gibi Ama findörneğin, o ise bir komut boş byte çıktısını ve bu bayt girdi olarak okur başka bir birliğe yöneltilen için mümkün müdür. Bunun hiçbir kısmı, Bash içindeki bir dizgede boş bir bayt depolamaya dayanmaz . İkinci örneğinizle ilgili tek sorun, $'\0'bir komutun argümanında kullanamamız ; echo "$file"$'\0'Sonunda boş byte'ı mutlu bir şekilde yazdırabilirdi.

Dolayısıyla kullanmak yerine, stil dizeleriyle aynı türde kaçış dizilerini destekleyen echokullanabilirsiniz . Bu şekilde, bir dize içinde boş bir bayta sahip olmak zorunda kalmadan boş bir bayt yazdırabilirsiniz. Bu şuna benzerdi:printf$'...'

for file in * ; do printf '%s\0' "$file" ; done \
  | while IFS= read -r -d '' ; do echo "$REPLY" ; done

ya da sadece bu:

printf '%s\0' * \
  | while IFS= read -r -d '' ; do echo "$REPLY" ; done

(Not: echoaslında boş bir baytı -eişlemesine \0ve yazdırmasına izin verecek bir bayrak da vardır ; ancak dosya adınızdaki herhangi bir özel diziyi işlemeye de çalışacaktır. Bu nedenle printfyaklaşım daha sağlamdır.)


Bu arada, bazı kabukları vardır do boş iç dizeleri bayt izin verir. Örneğin, Zsh'de örneğin çalışır (varsayılan ayar varsayarak). Bununla birlikte, kabuğunuzdan bağımsız olarak, Unix benzeri işletim sistemleri programlara argümanların içine boş baytlar eklemek için bir yol sağlamaz (çünkü program argümanları C tarzı dizgiler olarak iletilir), bu nedenle her zaman bazı sınırlamalar olacaktır. Çünkü sadece (Sizin örnek Zsh çalışabilir echobir kabuk yerleşiğidir Zsh diğer programları çağırmak için OS desteğine güvenerek olmadan çağırmak, böylece. Eğer kullandıysanız command echoyerine echo, bu yerleşiğini atlanır ve bağımsız kullanıldığı böylece echoprogramı $PATH, Zsh'de aynı davranışı Bash ile aynı şekilde görürsünüz.)


2
-d ''Sınırlandırmak için zaten bir araç varsa, IFS neden hiçbir şeye ayarlanmamış \0? Burada bir açıklama buldum: stackoverflow.com/questions/8677546/…
CMCDragonkai
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.