कैसे ठीक से zsh में लाइनों की एक सरणी इकट्ठा करने के लिए


42

मैंने सोचा कि निम्नलिखित my_commandलाइनों के एक समूह में आउटपुट का समूह होगा :

IFS='\n' array_of_lines=$(my_command);

ताकि $array_of_lines[1]के उत्पादन में पहली पंक्ति को उल्लेख करता है my_command, $array_of_lines[2]इसके आगे दूसरे के लिए, और।

हालाँकि, ऊपर की कमांड अच्छी तरह से काम नहीं करती है। यह my_commandचरित्र के चारों ओर के आउटपुट को विभाजित करने के लिए भी प्रतीत होता है n, जैसा कि मैंने के साथ जाँच की है print -l $array_of_lines, जो मुझे लगता है कि लाइन द्वारा एक सरणी लाइन के तत्वों को प्रिंट करता है। मैंने भी इसके साथ जाँच की है:

echo $array_of_lines[1]
echo $array_of_lines[2]
...

दूसरे प्रयास में, मुझे लगा कि evalइससे मदद मिल सकती है:

IFS='\n' array_of_lines=$(eval my_command);

लेकिन मुझे इसके बिना भी ठीक वैसा ही परिणाम मिला।

अंत में, zsh में रिक्त स्थान के साथ सूची तत्वों पर उत्तर के बाद , मैंने भी IFSzsh को इनपुट को विभाजित करने और तत्वों को एक सरणी में इकट्ठा करने के तरीके के बजाय पैरामीटर विस्तार झंडे का उपयोग करने की कोशिश की है , अर्थात:

array_of_lines=("${(@f)$(my_command)}");

लेकिन मुझे अभी भी वही परिणाम मिला है (विभाजन हो रहा है n)

इसके साथ, मेरे पास निम्नलिखित प्रश्न हैं:

Q1। लाइनों के एक सरणी में कमांड के आउटपुट को इकट्ठा करने के "उचित" तरीके क्या हैं ?

Q2। मैं IFSकेवल newlines पर विभाजन कैसे निर्दिष्ट कर सकता हूं ?

Q3। अगर मैं @fस्प्लिटिंग को निर्दिष्ट करने के लिए ऊपर (अर्थात उपयोग करके ) के रूप में पैरामीटर विस्तार झंडे का उपयोग करता हूं, तो क्या zsh मूल्य को अनदेखा करता है IFS? यह ऊपर काम क्यों नहीं किया?

जवाबों:


71

टीएल, डीआर:

array_of_lines=("${(@f)$(my_command)}")

पहली गलती (→ Q2): दो पात्रों के लिए IFS='\n'सेट और । एक नई रेखा पर सेट करने के लिए, का उपयोग करें ।IFS\nIFSIFS=$'\n'

दूसरी गलती: सरणी मान के लिए एक चर सेट करने के लिए, आपको तत्वों के चारों ओर कोष्ठक की आवश्यकता है array_of_lines=(foo bar):।

यह काम करेगा, सिवाय इसके कि यह खाली लाइनों को स्ट्रिप करता है, क्योंकि एक ही विभाजक के रूप में लगातार व्हॉट्सएप मायने रखता है:

IFS=$'\n' array_of_lines=($(my_command))

आप व्हाट्सएप के चरित्र को दोगुना करके बहुत अंत में खाली लाइनों को बनाए रख सकते हैं IFS:

IFS=$'\n\n' array_of_lines=($(my_command))

साथ ही खाली लाइनों को ट्रेस करने के लिए, आपको कमांड के आउटपुट में कुछ जोड़ना होगा, क्योंकि यह कमांड प्रतिस्थापन में होता है, इसे पार्स करने से नहीं।

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'

( my_commandगैर-सीमांकित पंक्ति में आउटपुट समाप्त नहीं होता है, यह भी ध्यान दें कि आप बाहर निकलने की स्थिति खो देते हैं my_command)

ध्यान दें कि उपरोक्त सभी स्निपेट्स IFSइसके गैर-डिफ़ॉल्ट मान के साथ छोड़ देते हैं , इसलिए वे बाद के कोड को गड़बड़ कर सकते हैं। IFSस्थानीय की सेटिंग रखने के लिए , पूरी चीज़ को एक फंक्शन में रखें जहाँ आप IFSलोकल घोषित करें (यहाँ कमांड के एक्ज़िट स्टेटस को सुरक्षित रखने का भी ध्यान रखें):

collect_lines() {
  local IFS=$'\n\n' ret
  array_of_lines=($("$@"; ret=$?; echo .; exit $ret))
  ret=$?
  unset 'array_of_lines[-1]'
  return $ret
}
collect_lines my_command

लेकिन मैं इसके साथ खिलवाड़ नहीं करने की सलाह देता हूं IFS; इसके बजाय, fन्यूलाइंस (→ Q1) पर विभाजित करने के लिए विस्तार ध्वज का उपयोग करें :

array_of_lines=("${(@f)$(my_command)}")

या अनुगामी खाली लाइनों को संरक्षित करने के लिए:

array_of_lines=("${(@f)$(my_command; echo .)}")
unset 'array_of_lines[-1]'

का मूल्य IFSवहाँ कोई फर्क नहीं पड़ता। मुझे संदेह है कि आपने अपने परीक्षण (→ Q3) में IFSप्रिंट करने के लिए विभाजन वाली एक कमांड का उपयोग किया था $array_of_lines


7
यह बहुत जटिल है! "${(@f)...}"के रूप में ही है ${(f)"..."}, लेकिन एक अलग तरीके से। (@)अंदर दोहरे कोट्स का अर्थ है "एरे एलीमेंट एलीमेंट एलिमेंट एलीमेंट एलिमेंट" और (f)इसका मतलब है "न्यूलाइन द्वारा एरे में विभाजित होना"। पुनश्च: डॉक्स के
उड़ान भेड़

3
@flyingsheep, कोई ${(f)"..."}भी खाली लाइनों को नहीं छोड़ता, "${(@f)...}"उन्हें संरक्षित करता है। यही अंतर है $argvऔर "$argv[@]"। यही कारण है कि "$@"बात यह है कि एक सरणी के सभी तत्वों को संरक्षित करने के लिए देर से 70 के दशक में बॉर्न शैल से आता है।
स्टीफन चेज़लस

4

दो मुद्दे: पहला, जाहिरा तौर पर दोहरे उद्धरण भी बैकस्लैश एस्केप (उस के बारे में खेद :) की व्याख्या नहीं करते हैं। $'...'उद्धरण का उपयोग करें। और के अनुसार man zshparam, एक सरणी में शब्दों को इकट्ठा करने के लिए आपको उन्हें कोष्ठक में संलग्न करना होगा। तो यह काम करता है:

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
c
d
e f
% print $arr[1]
a b

मैं आपके Q3 का जवाब नहीं दे सकता। मुझे आशा है कि मुझे इस तरह की गूढ़ बातों को कभी नहीं जानना पड़ेगा :)।


-2

तुम भी अंतरिक्ष के साथ newline को बदलने के लिए tr का उपयोग कर सकते हैं:

lines=($(mycommand | tr '\n' ' '))
select line in ("${lines[@]}"); do
  echo "$line"
  break
done

3
क्या होगा अगर लाइनों में रिक्त स्थान हैं?
don_crissti

2
इसका कोई अर्थ नही बन रहा है। SPC और NL दोनों डिफ़ॉल्ट मान में हैं $IFS। एक से दूसरे में अनुवाद करने से कोई फर्क नहीं पड़ता।
स्टीफन चेज़लस

क्या मेरे संपादन उचित थे? मुझे यह काम करने के लिए नहीं मिला जैसा कि यह था,
जॉन पी

(समय समाप्त) मैं मानता हूं कि मैंने इसे वास्तव में समझने के बिना संपादित किया कि इरादा क्या था, लेकिन मुझे लगता है कि स्ट्रिंग हेरफेर के आधार पर जुदाई के लिए अनुवाद एक अच्छी शुरुआत है। मैं रिक्त स्थान पर अनुवाद नहीं करूंगा, और उन पर विभाजित नहीं करूंगा, जब तक कि अपेक्षित व्यवहार अधिक पसंद नहीं किया गया था echo, जहां इनपुट सभी या कम शब्दों के ढेर हैं जो परवाह करता है कि कौन अलग है।
जॉन पी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.