Bash korzysta wewnętrznie z ciągów w stylu C, które kończą się bajtami zerowymi. Oznacza to, że ciąg Bash (taki jak wartość zmiennej lub argument polecenia) nigdy nie może zawierać bajtu zerowego. Na przykład ten mini-skrypt:
foobar=$'foo\0bar' # foobar='foo' + null byte + 'bar'
echo "${#foobar}" # print length of $foobar
faktycznie drukuje 3
, bo $foobar
tak naprawdę jest 'foo'
: bar
przychodzi po końcu łańcucha.
Podobnie, echo $'foo\0bar'
po prostu drukuje foo
, ponieważ echo
nie wie o \0bar
części.
Jak widać, \0
sekwencja jest w rzeczywistości bardzo myląca w $'...'
łańcuchu w stylu; wygląda jak bajt zerowy w ciągu, ale nie działa w ten sposób. W pierwszym przykładzie twoje read
polecenie ma -d $'\0'
. To działa, ale tylko dlatego, że -d ''
działa! (To nie jest wyraźnie udokumentowana cecha read
, ale przypuszczam, że działa z tego samego powodu: ''
jest pustym łańcuchem, więc jego końcowy bajt zerowy pojawia się natychmiast. Jest udokumentowany jako „Pierwszy znak delim ” i myślę, że nawet działa jeśli „pierwszy znak” znajduje się poza końcem ciągu!)-d delim
Ale jak wiadomo z find
przykładu, to jest możliwe, polecenie, aby wydrukować zerowy bajt, a do tego bajt być wyprowadzony do innego polecenia, które odczytuje go jako wejście. Żadna część tego nie polega na przechowywaniu pustego bajtu w ciągu wewnątrz Bash . Jedynym problemem związanym z drugim przykładem jest to, że nie możemy użyć $'\0'
argumentu do polecenia; echo "$file"$'\0'
mógłby z radością wydrukować bajt zerowy na końcu, gdyby tylko wiedział, że tego chcesz.
Zamiast używać echo
, możesz użyć printf
, który obsługuje takie same sekwencje specjalne jak $'...'
łańcuchy-style. W ten sposób możesz wydrukować bajt zerowy bez konieczności umieszczania bajtu zerowego w ciągu. To by wyglądało tak:
for file in * ; do printf '%s\0' "$file" ; done \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
lub po prostu to:
printf '%s\0' * \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
(Uwaga: w echo
rzeczywistości ma także -e
flagę, która pozwoli mu przetworzyć \0
i wydrukować bajt zerowy; ale wtedy spróbuje również przetworzyć specjalne sekwencje w nazwie pliku. Więc printf
podejście jest bardziej niezawodne).
Nawiasem mówiąc, istnieje kilka powłok, które dopuszczają bajty puste w łańcuchach. Twój przykład działa dobrze w Zsh, na przykład (przy domyślnych ustawieniach). Jednak, niezależnie od powłoki, systemy operacyjne uniksowe nie zapewniają sposobu na umieszczenie pustych bajtów wewnątrz argumentów w programach (ponieważ argumenty programu są przekazywane jako ciągi w stylu C), więc zawsze będą pewne ograniczenia. (Twój przykład może działać w Zsh tylko dlatego, że echo
jest wbudowany w powłokę, więc Zsh może go wywoływać bez polegania na wsparciu systemu operacyjnego w przypadku wywoływania innych programów. Jeśli użyłeś command echo
zamiast tego echo
, aby ominął wbudowane i używał samodzielnego echo
programu w $PATH
, zobaczysz to samo zachowanie w Zsh jak w Bash.)
-d ''
już oznacza ograniczenie\0
? Znalazłem tu wyjaśnienie: stackoverflow.com/questions/8677546/…