मैं बैश में नल बाइट्स का उपयोग कैसे करूं?


33

मैंने पढ़ा है कि, चूंकि बाश में फ़ाइल-पथ में नल बाइट (शून्य-मूल्यवान बाइट $'\0') को छोड़कर कोई भी चरित्र हो सकता है , इसलिए यह एक विभाजक के रूप में नल बाइट का उपयोग करना सबसे अच्छा है। उदाहरण के लिए, यदि आउटपुट findकिसी अन्य प्रोग्राम में भेजा जाएगा, तो -print0विकल्प का उपयोग करने की अनुशंसा की जाती है (इसके संस्करणों के लिए find)।

लेकिन यद्यपि ऐसा कुछ ठीक काम करता है (नई-नई कथनों द्वारा अलग किए गए फ़ाइल-पथ प्रिंट करना - चिंता न करें, यह केवल एक प्रदर्शन है, मैं वास्तव में इसे वास्तविक स्क्रिप्ट में नहीं कर रहा हूं):

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

ऐसा कुछ काम नहीं करता है:

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

जब मैं सिर्फ for-लूप भाग की कोशिश करता हूं, तो मुझे पता चलता है कि यह सभी फिल्नामों को एक साथ प्रिंट करता है, बीच में बिना बाइट के।

ऐसा क्यों है? क्या चल रहा है?

जवाबों:


43

बैश आंतरिक रूप से सी-स्टाइल स्ट्रिंग्स का उपयोग करता है, जिन्हें नल बाइट्स द्वारा समाप्त किया जाता है। इसका मतलब यह है कि एक बैश स्ट्रिंग (जैसे कि एक चर का मान, या एक कमांड का तर्क) वास्तव में एक अशक्त बाइट नहीं हो सकता है। उदाहरण के लिए, यह मिनी स्क्रिप्ट:

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

वास्तव में प्रिंट करता है 3, क्योंकि $foobarवास्तव में बस है 'foo': barस्ट्रिंग के अंत के बाद आता है।

इसी तरह, echo $'foo\0bar'सिर्फ प्रिंट foo, क्योंकि भाग के echoबारे में पता नहीं है \0bar

जैसा कि आप देख सकते हैं, \0अनुक्रम वास्तव में एक $'...'इनस्टाइल स्ट्रिंग में बहुत भ्रामक है ; यह स्ट्रिंग के अंदर एक अशक्त बाइट की तरह दिखता है, लेकिन यह इस तरह से काम नहीं करता है। आपके पहले उदाहरण में, आपकी readआज्ञा है -d $'\0'। यह काम करता है, लेकिन केवल इसलिए -d ''भी काम करता है! (यह स्पष्ट रूप से प्रलेखित विशेषता नहीं है read, लेकिन मुझे लगता है कि यह एक ही कारण के लिए काम करता है: ''खाली स्ट्रिंग है, इसलिए इसकी समाप्ति नल बाइट तुरंत आती है। इसे " डेलिम के पहले चरित्र" का उपयोग करने के रूप में प्रलेखित किया गया है , और मुझे लगता है कि यह काम करता है। अगर "पहला चरित्र" स्ट्रिंग के अंत में है! "-d delim

लेकिन जैसा कि आप अपने से पता findउदाहरण के लिए, यह है एक कमांड एक अशक्त बाइट प्रिंट करने के लिए, और कहा कि बाइट एक और आदेश है कि यह इनपुट के रूप में पढ़ता को पहुंचाया जा करने के लिए के लिए संभव। उस का कोई हिस्सा बैश के अंदर एक स्ट्रिंग में एक अशक्त बाइट भंडारण पर निर्भर करता है । आपके दूसरे उदाहरण के साथ एकमात्र समस्या यह है कि हम $'\0'कमांड के तर्क में उपयोग नहीं कर सकते हैं ; echo "$file"$'\0'खुशी से अंत में नल बाइट प्रिंट कर सकता है, अगर केवल यह जानता था कि आप इसे चाहते थे।

इसलिए उपयोग करने के बजाय echo, आप उपयोग कर सकते हैं printf, जो $'...'-स्टाइल स्ट्रिंग्स के रूप में एक ही तरह के एस्केप सीक्वेंस का समर्थन करता है । इस तरह, आप एक स्ट्रिंग के अंदर एक शून्य बाइट के बिना एक नल बाइट मुद्रित कर सकते हैं। यह इस तरह दिखेगा:

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

या बस यह:

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

(ध्यान दें: echoवास्तव में एक -eध्वज भी होता है जो इसे \0एक सुस्त बाइट को संसाधित करने और प्रिंट करने देता है; लेकिन फिर यह आपके फ़ाइल नाम में किसी विशेष क्रम को संसाधित करने का भी प्रयास करेगा। इसलिए printfदृष्टिकोण अधिक मजबूत है।)


संयोग से, वहाँ कुछ गोले कि कर रहे हैं करते अशक्त अंदर तार बाइट्स अनुमति देते हैं। आपका उदाहरण Zsh में ठीक काम करता है, उदाहरण के लिए (डिफ़ॉल्ट सेटिंग्स मानकर)। हालाँकि, आपके शेल की परवाह किए बिना, यूनिक्स-जैसे ऑपरेटिंग सिस्टम, प्रोग्राम के लिए तर्कों के अंदर नल बाइट्स को शामिल करने का एक तरीका प्रदान नहीं करते हैं (क्योंकि प्रोग्राम तर्क को सी-स्टाइल स्ट्रिंग्स के रूप में पारित किया जाता है), इसलिए हमेशा कुछ सीमाएं होंगी। (आपका उदाहरण केवल Zsh में काम कर सकता है क्योंकि echoएक शेल बिल्डिन है, इसलिए Zsh अन्य कार्यक्रमों को लागू करने के लिए OS समर्थन पर भरोसा किए बिना इसे लागू कर सकता है। यदि आप command echoइसके बजाय उपयोग करते हैं echo, ताकि यह बिलिन को बायपास कर दे और स्टैंडअलोन echoप्रोग्राम का उपयोग कर सके $PATH, आप ज़श में वैसा ही व्यवहार देखेंगे जैसे बैश में।)


2
यदि -d ''पहले से ही परिसीमन करने का मतलब है तो IFS कुछ भी क्यों नहीं सेट करता है \0? मुझे यहाँ एक स्पष्टीकरण मिला: stackoverflow.com/questions/8677546/…
CMCDragonkai
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.