$ '\ 0' '' के समान क्यों है?


10

फ़ाइलों के एक जोड़े के साथ काम करने का एक सामान्य तरीका है - और उसके लिए मुझे मत मारो:

for f in $(ls); do 

अब, रिक्त स्थान या अन्य अजीब पात्रों के साथ फ़ाइलों के खिलाफ सुरक्षित होने के लिए, एक भोला तरीका करना होगा:

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

यहां, -d ''ASCII NUL को इस रूप में सेट करने के लिए छोटा है -d $'\0'

लेकिन ऐसा क्यों है? क्यों ''और $'\0'एक ही हैं? क्या बैश की सी जड़ों के कारण एक खाली तार हमेशा शून्य-समाप्त होता है?


"भोले" तरीके का उल्लेख करते हुए, क्या ऐसा करने का एक बेहतर तरीका है?
इरूवर

2
वैसे यदि आप फ़ाइलों के सेट पर सुरक्षित संचालन करना चाहते हैं - for f in *पार्स करने के बजाय उपयोग करें ls

@ मुझे पता for i in $(ls)है कि मैं बहुत बेवकूफ हूं- मैं लगभग शर्मिंदा हूं मैंने इसका इस्तेमाल यहां एक बुरे उदाहरण के रूप में किया है।
१३:१३ पर १३:१३

@ChandraRavoori हाँ, उदाहरण के लिए find … -execफ़ाइलों के चारों ओर लूपिंग के बजाय का उपयोग करके , जो अधिकांश मामलों के लिए काम करता है जहां आप लूप के बजाय ऐसे उपयोग करेंगे। यहाँ, findआप के लिए सब कुछ का ख्याल रखता है।
16

@ श्लोक, धन्यवाद। प्रत्येक फ़ाइल पर बहु-चरण संचालन से संबंधित स्थितियों के बारे में क्या जहां पठनीयता कारणों के लिए एक लूप बेहतर हो सकता है? क्या ऊपर "भोले रास्ते" से बेहतर लूप विकल्प है?
इरूवर

जवाबों:


10

man page of bashपढ़ता है:

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

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

स्रोत पढ़ता है:

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

एक खाली स्ट्रिंग के delimलिए बस नल बाइट है।


जब आप कहते हैं कि "तार आमतौर पर शून्य समाप्त हो जाते हैं", तो क्या यह स्थिति POSIX वातावरण में कहीं नहीं है? उन दिनों से जब मैं स्कूल के लिए सी सीख रहा था, निश्चित रूप से यह ऐसा मान लेता है; मैं बस जाँच कर रहा था।
स्लैक

लेकिन कोई भी किसी भी स्ट्रिंग को मनमाने ढंग से कई खाली तारों से युक्त मान सकता है, जैसे कि यदि आप '' और "एक्स" को "एक्स" प्राप्त करते हैं। तो आप तर्क कर सकते हैं कि पहला विकल्प बैश एनकाउंटर खाली स्ट्रिंग है। उदाहरण के लिए यदि आप जावास्क्रिप्ट में खाली स्ट्रिंग का उपयोग करते हैं तो split()यह प्रत्येक वर्ण के बीच विभाजित हो जाएगा। मुझे संदेह है कि "ऐतिहासिक कारणों से" सबसे अच्छा स्पष्टीकरण हो सकता है जो हम प्राप्त कर सकते हैं।
डोनोथिंग्स असफल

खैर, काफी नहीं है, क्योंकि "श्रृंखलाबद्ध" एक सी शैली '\0'के साथ 'X\0'आप देना चाहिए 'X\0', अगर किया सही। यह बहुत तरह के रूप में जावास्क्रिप्ट @don भाषाओं में उच्च स्तर के कार्यों से कोई लेना देना नहीं है
slhck

धन्यवाद, मीशा, स्रोत जोड़ने के लिए। delim = *list_optarg;यह स्पष्ट करता है कि ऐसा क्यों है।
slhck

@ श्लोक: क्षमा करें, मैंने खुद को स्पष्ट नहीं किया। आपने पूछा "क्यों ''और $'\0'वही हैं?", माइकस ने "यह वही है जो कोड करता है" का अनुमानित विवरण दिया। मैंने खाली स्ट्रिंग को संभालने के एक वैकल्पिक तरीके को रेखांकित किया, जिसे मैंने समान रूप से उचित देखा और सुझाव दिया कि एक या दूसरे को चुनना केवल सम्मेलन या प्रतिष्ठा का विषय था।
डोनोथिंग्स असफल

6

बैश में दो कमियां हैं जो एक दूसरे की भरपाई करती हैं।

जब आप लिखते हैं $'\0', कि आंतरिक रूप से खाली स्ट्रिंग के लिए समान रूप से व्यवहार किया जाता है। उदाहरण के लिए:

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

ऐसा इसलिए है क्योंकि आंतरिक रूप से सभी स्ट्रिंग्स को सी स्ट्रिंग्स के रूप में स्टोर किया जाता है , जो शून्य-समाप्त होते हैं - एक अशक्त बाइट स्ट्रिंग के अंत को चिह्नित करता है। बैश चुपचाप स्ट्रिंग को पहले नल बाइट (जो स्ट्रिंग का हिस्सा नहीं है!) को काट देता है।

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

जब आप एक स्ट्रिंग को बेसिन के -dविकल्प के तर्क के रूप में पास करते हैं read, तो बैश केवल स्ट्रिंग के पहले बाइट को देखता है। लेकिन यह वास्तव में जांच नहीं करता है कि स्ट्रिंग खाली नहीं है। आंतरिक रूप से, एक रिक्त स्ट्रिंग को 1-तत्व बाइट सरणी के रूप में दर्शाया जाता है जिसमें बस एक अशक्त बाइट होता है। इसलिए स्ट्रिंग के पहले बाइट को पढ़ने के बजाय, बैश इस नल बाइट को पढ़ता है।

फिर, आंतरिक रूप से, readबिलिन के पीछे की मशीनरी नल बाइट्स के साथ अच्छी तरह से काम करती है; यह बाइट तब तक बाइट पढ़ता रहता है जब तक कि यह सीमांकक न मिल जाए।

अन्य गोले अलग व्यवहार करते हैं। उदाहरण के लिए, जब वे इनपुट पढ़ते हैं, तो ऐश और ksh नल बाइट्स को अनदेखा कर देते हैं। Ksh के साथ, ksh -d ""एक नई पंक्ति तक पढ़ता है। गोले को पाठ के साथ अच्छी तरह से सामना करने के लिए डिज़ाइन किया गया है, न कि बाइनरी डेटा के साथ। Zsh एक अपवाद है: यह एक स्ट्रिंग प्रतिनिधित्व का उपयोग करता है जो मनमाने बाइट्स के साथ मुकाबला करता है, जिसमें नल बाइट्स भी शामिल है; zsh में, $'\0'लंबाई 1 (लेकिन read -d '', अजीब तरह से व्यवहार करता है read -d $'\0') की एक स्ट्रिंग है ।


readबाश 4.3 में बदले हुए व्यवहार के कारण यह अब सुस्त बाइट्स को छोड़ देता है। उदाहरण के लिए इसके बजाय read x< <(printf a\\0a)सेट करता xहै । aaa
लैरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.