Bash sử dụng các chuỗi kiểu C bên trong, được kết thúc bằng các byte rỗng. Điều này có nghĩa là một chuỗi Bash (chẳng hạn như giá trị của biến hoặc đối số cho lệnh) thực sự không bao giờ có thể chứa byte rỗng. Ví dụ: tập lệnh nhỏ này:
foobar=$'foo\0bar' # foobar='foo' + null byte + 'bar'
echo "${#foobar}" # print length of $foobar
thực sự in 3
, bởi vì $foobar
thực sự chỉ là 'foo'
: bar
đến sau khi kết thúc chuỗi.
Tương tự, echo $'foo\0bar'
chỉ in foo
, bởi vìecho
không biết về \0bar
phần này.
Như bạn có thể thấy, \0
chuỗi thực sự rất sai lệch trong $'...'
chuỗi kiểu; nó trông giống như một byte rỗng bên trong chuỗi, nhưng nó không hoạt động theo cách đó. Trong ví dụ đầu tiên của bạn, read
lệnh của bạn có -d $'\0'
. Điều này hoạt động, nhưng chỉ vì -d ''
cũng hoạt động! (Đó không phải là một tính năng ghi nhận một cách rõ ràng về read
, nhưng tôi cho rằng nó hoạt động với cùng lý do: ''
là chuỗi rỗng, vì vậy chấm dứt vô byte của nó đến ngay lập tức. Là tài liệu như sử dụng "Ký tự đầu tiên của dấu phân cách ", và tôi đoán rằng tác phẩm thậm chí nếu "ký tự đầu tiên" ở cuối chuỗi!)-d delim
Nhưng như bạn đã biết từ của bạn find
Ví dụ, nó là có thể cho một lệnh để in ra một byte null, và cho byte đó để được đường ống để một lệnh mà đọc nó như là đầu vào. Không có phần nào trong đó phụ thuộc vào việc lưu trữ một byte null trong một chuỗi bên trong Bash . Vấn đề duy nhất với ví dụ thứ hai của bạn là chúng tôi không thể sử dụng$'\0'
trong một đối số cho một lệnh; echo "$file"$'\0'
có thể vui vẻ in byte null ở cuối, nếu chỉ có nó biết rằng bạn muốn nó.
Vì vậy, thay vì sử dụng echo
, bạn có thể sử dụng printf
, hỗ trợ các chuỗi thoát tương tự như $'...'
chuỗi kiểu. Bằng cách đó, bạn có thể in một byte null mà không cần phải có byte rỗng bên trong chuỗi. Điều đó sẽ trông như thế này:
for file in * ; do printf '%s\0' "$file" ; done \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
hoặc đơn giản là thế này:
printf '%s\0' * \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
(Lưu ý: echo
thực sự cũng có một -e
cờ cho phép nó xử lý \0
và in một byte null; nhưng sau đó nó cũng sẽ cố gắng xử lý bất kỳ chuỗi đặc biệt nào trong tên tệp của bạn.printf
cách tiếp cận mạnh mẽ hơn.)
Ngẫu nhiên, có một số vỏ mà làm phép null byte chuỗi bên trong. Ví dụ của bạn hoạt động tốt trong Zsh, ví dụ (giả sử cài đặt mặc định). Tuy nhiên, bất kể hệ vỏ của bạn là gì, các hệ điều hành giống Unix không cung cấp cách bao gồm các byte rỗng bên trong các đối số cho các chương trình (vì các đối số chương trình được truyền dưới dạng chuỗi kiểu C), do đó sẽ luôn có một số hạn chế. (Ví dụ của bạn chỉ có thể hoạt động trong Zsh vì echo
được tích hợp shell, vì vậy Zsh có thể gọi nó mà không cần dựa vào sự hỗ trợ của HĐH để gọi các chương trình khác. Nếu bạn sử dụng command echo
thay vì echo
, nó đã bỏ qua phần dựng sẵn và sử dụng echo
chương trình độc lập trên $PATH
, bạn sẽ thấy hành vi tương tự trong Zsh như trong Bash.)
-d ''
đã có nghĩa là phân định\0
? Tôi tìm thấy một lời giải thích ở đây: stackoverflow.com/questions/8677546/