Mengapa $ '\ 0' sama dengan ''?


10

Cara umum untuk melakukan sesuatu dengan beberapa file adalah — dan jangan memukul saya untuk itu:

for f in $(ls); do 

Sekarang, untuk aman terhadap file dengan spasi atau karakter aneh lainnya, cara naif adalah dengan melakukannya:

find . -type f -print0 | while IFS= read -r -d '' file; 

Di sini, -d ''kependekan dari pengaturan NUL ASCII seperti pada -d $'\0'.

Tapi mengapa begitu? Mengapa demikian ''dan $'\0'sama? Apakah itu karena akar C dari Bash dengan string kosong selalu diakhiri null?


Mengacu pada cara "naif", apakah ada cara yang lebih baik untuk melakukan ini?
iruvar

2
By the way jika Anda ingin melakukan operasi yang aman iterasi lebih dari satu set file - penggunaan for f in *bukan parsing ls.

@htor yang saya tahu for i in $(ls)sangat bodoh — saya hampir malu saya menggunakannya sebagai contoh yang buruk di sini.
slhck

@ ChandraRavoori Ya, misalnya dengan menggunakan find … -execalih-alih memutar di sekitar file, yang berfungsi untuk sebagian besar kasus di mana Anda akan menggunakan loop untuk. Di sini, findurus semuanya untuk Anda.
slhck

Sslhck, terima kasih. Bagaimana dengan situasi yang melibatkan operasi multi-langkah pada setiap file di mana loop mungkin lebih disukai karena alasan keterbacaan? Apakah ada opsi loop yang lebih baik daripada "cara naif" di atas?
iruvar

Jawaban:


10

The man page of bashberbunyi:

          -d delim
                 The first character of delim is  used  to  terminate  the
                 input line, rather than newline.

Karena string biasanya null diakhiri, karakter pertama dari string kosong adalah byte nol. - Masuk akal bagiku. :)

Sumbernya berbunyi:

static unsigned char delim;
[...]
    case 'd':
      delim = *list_optarg;
      break;

Untuk string kosong delimhanyalah byte nol.


Ketika Anda mengatakan "string biasanya null dihentikan", bukankah itu terjadi di suatu lingkungan POSIX? Dari hari-hari ketika saya belajar C untuk sekolah, tentu saja masuk akal untuk berasumsi demikian; Saya baru saja memeriksa.
slhck

Tetapi orang dapat menganggap string apa pun sebagai berisi banyak string kosong, misalnya jika Anda menggabungkan '' dan "X" Anda mendapatkan "X". Jadi Anda bisa berargumen bahwa bash substring perjumpaan pertama adalah string kosong. Sebagai contoh jika Anda menggunakan string kosong di javascript split()itu akan dibagi antara setiap karakter. Saya menduga "karena alasan historis" mungkin merupakan penjelasan terbaik yang bisa kita dapatkan.
Donasi berhasil

Ya, tidak cukup karena "menggabungkan" gaya C '\0'dengan 'X\0'seharusnya memberi Anda 'X\0', jika dilakukan dengan benar. Ini tidak ada hubungannya dengan fungsi tingkat tinggi dalam bahasa seperti JavaScript @don
slhck

Terima kasih, michas, untuk menambahkan sumbernya. delim = *list_optarg;membuatnya jelas mengapa demikian.
slhck

@ Slhck: Maaf, saya tidak menjelaskan. Anda bertanya "mengapa ''dan $'\0'sama?", Michas memberikan penjelasan langsung tentang "itulah yang dilakukan kode". Saya menguraikan cara alternatif menangani string kosong yang saya lihat sama masuk akal dan menyarankan bahwa memilih satu atau yang lain hanyalah masalah konvensi atau kebetulan.
Donasi berhasil

6

Ada dua kekurangan dalam bash yang saling memberi kompensasi.

Ketika Anda menulis $'\0', itu diperlakukan secara internal identik dengan string kosong. Sebagai contoh:

$ a=$'\0'; echo ${#a}
0

Itu karena secara internal bash menyimpan semua string sebagai string C , yang diakhiri null - byte nol menandai akhir string. Bash secara diam-diam memotong string ke byte nol pertama (yang bukan merupakan bagian dari string!).

# a=$'foo\0bar'; echo "$a"; echo ${#a}
foo
3

Saat Anda meneruskan string sebagai argumen ke -dopsi readbuiltin, bash hanya melihat byte pertama dari string. Tetapi tidak benar-benar memeriksa bahwa string tidak kosong. Secara internal, string kosong direpresentasikan sebagai array byte 1-elemen yang hanya berisi byte nol. Jadi alih-alih membaca byte pertama dari string, bash membaca byte nol ini.

Kemudian, secara internal, mesin di belakang readbuiltin bekerja dengan baik dengan null byte; itu terus membaca byte demi byte sampai menemukan pembatas.

Kerang lainnya berperilaku berbeda. Misalnya, ash dan ksh mengabaikan byte nol ketika mereka membaca input. Dengan ksh, ksh -d ""dibaca hingga baris baru. Kerang dirancang untuk mengatasi dengan baik teks, bukan dengan data biner. Zsh adalah pengecualian: ia menggunakan representasi string yang berupaya dengan byte sewenang-wenang, termasuk byte nol; di zsh, $'\0'adalah string dengan panjang 1 (tapi read -d '', anehnya, berperilaku seperti read -d $'\0').


Perilaku readdiubah dalam bash 4.3 sehingga sekarang melompati null byte. Misalnya read x< <(printf a\\0a)set xke aabukan a.
Lri
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.