BASH साहचर्य सरणी मुद्रण


18

क्या सभी तत्वों पर नियंत्रण के बिना संपूर्ण सरणी ([कुंजी] = मान) मुद्रित करने का कोई तरीका है?

मान लें कि मैंने कुछ तत्वों के साथ एक सरणी बनाई है:

declare -A array
array=([a1]=1 [a2]=2 ... [b1]=bbb ... [f500]=abcdef)

मैं पूरे ऐरे से वापस प्रिंट कर सकता हूं

for i in "${!array[@]}"
do
echo "${i}=${array[$i]}"
done

हालांकि, ऐसा लगता है कि बैश पहले से ही जानता है कि सभी सरणी तत्वों को एक "गो" में कैसे प्राप्त किया जाए - कुंजी ${!array[@]}और मूल्य दोनों ${array[@]}

क्या लूप के बिना इस जानकारी को प्रिंट करने का एक तरीका है?

संपादित करें:
typeset -p arrayवह करता है!
हालाँकि मैं एक ही प्रतिस्थापन में उपसर्ग और प्रत्यय दोनों नहीं निकाल सकता:

a="$(typeset -p array)"
b="${a##*(}"
c="${b%% )*}"

क्या आउटपुट का केवल कुंजी = मान भाग प्राप्त करने / प्रिंट करने के लिए कोई क्लीनर तरीका है?

जवाबों:


16

मुझे लगता है कि आप वहां दो अलग चीजें पूछ रहे हैं।

क्या लूप के बिना इस जानकारी को प्रिंट करने का एक तरीका है?

हां, लेकिन वे केवल लूप का उपयोग करने के रूप में अच्छे नहीं हैं।

क्या आउटपुट का केवल कुंजी = मान भाग प्राप्त करने / प्रिंट करने के लिए कोई क्लीनर तरीका है?

हाँ, forपाश। इसके फायदे हैं कि इसके लिए बाहरी कार्यक्रमों की आवश्यकता नहीं है, यह सीधा है, और यह आश्चर्य के बिना सटीक आउटपुट प्रारूप को नियंत्रित करना आसान बनाता है।


कोई भी समाधान जो declare -p( typeset -p) के आउटपुट को संभालने की कोशिश करता है ( क) को स्वयं को वैरिएबल की संभावना जो कि कोष्ठक या कोष्ठक से युक्त है, बी) वह उद्धरण जो declare -pइसे शेल के लिए आउटपुट मान्य इनपुट बनाने के लिए जोड़ना है।

उदाहरण के लिए, आपका विस्तार b="${a##*(}"कुछ मूल्यों को खाता है, यदि किसी भी कुंजी / मूल्य में एक प्रारंभिक कोष्ठक है। ऐसा इसलिए है क्योंकि आपने उपयोग किया है ##, जो सबसे लंबे समय तक उपसर्ग को हटा देता है । उसी के लिए c="${b%% )*}"। यद्यपि आप निश्चित रूप से declareअधिक सटीक रूप से मुद्रित बॉयलरप्लेट से मेल खा सकते हैं, फिर भी आपके पास एक कठिन समय होगा यदि आप नहीं चाहते कि यह सब उद्धृत किया जाए।

यह बहुत अच्छा नहीं लगता है जब तक कि आपको इसकी आवश्यकता न हो।

$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'

forलूप के साथ , जैसा कि आप चाहते हैं आउटपुट स्वरूप चुनना आसान है:

# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'

# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'

वहाँ से, आउटपुट फॉर्मेट को बदलना आसान है अन्यथा (कोष्ठक के चारों ओर के कोष्ठक हटा दें, सभी कुंजी / मान युग्मों को एक पंक्ति में रखें)। यदि आपको शेल के अलावा किसी अन्य चीज़ के लिए उद्धृत करने की आवश्यकता है, तो आपको अभी भी इसे स्वयं करने की आवश्यकता होगी, लेकिन कम से कम आपके पास काम करने के लिए कच्चा डेटा होगा। (यदि आपके पास कुंजियों या मूल्यों में नई रूपरेखाएँ हैं, तो आपको संभवतः कुछ उद्धरण की आवश्यकता होगी।)

वर्तमान बैश (4.4, मुझे लगता है) के साथ, आप printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"इसके बजाय भी उपयोग कर सकते हैं printf "%q=%q"। यह कुछ हद तक एक अच्छे उद्धरण स्वरूप का उत्पादन करता है, लेकिन निश्चित रूप से लिखने के लिए याद रखने के लिए थोड़ा अधिक काम है। (और यह @सरणी कुंजी के कोने मामले को %qउद्धृत करता है , जो उद्धरण नहीं करता है।)

अगर लिखने के लिए लूप बहुत थका हुआ लगता है, तो इसे कहीं फंक्शन में सेव करें (यहाँ उद्धृत किए बिना):

printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ;  }  

और फिर बस उस का उपयोग करें:

$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)

अनुक्रमित सरणियों के साथ भी काम करता है:

$ b=(abba acdc)
$ printarr b
0=abba
1=acdc

ध्यान दें कि आपके printf ...%q...वेरिएंट का आउटपुट शेल के लिए प्रबलित करने के लिए उपयुक्त नहीं है यदि सरणी में एक @कुंजी है क्योंकि% q इसे उद्धृत नहीं करता है और इसमें a=([@]=value)एक सिंटैक्स त्रुटि है bash
स्टीफन चेजेलस

@ स्टीफनचेजेलस, जाहिरा तौर पर। "${x@Q}"वह भी उद्धरण, क्योंकि यह सभी तार उद्धृत करता है (और अच्छे लगते हैं)। उस का उपयोग करने के बारे में एक नोट जोड़ा।
ilkachachu

हाँ, mksh से कॉपी किया गया। अभी तक एक अलग आकार का एक और ऑपरेटर जो अन्य लोगों के साथ जोड़ा नहीं जा सकता है। फिर से, zshइसके चर विस्तार झंडे के साथ देखें (जो फिर से दशकों तक बैश की भविष्यवाणी करता है और जिसके साथ आप बेहतर शैली के लिए उद्धरण शैली: $ {(q) var}, $ {(qq) var} ...) चुन सकते हैं। बैश में mksh जैसा ही मुद्दा है कि यह खाली स्ट्रिंग को उद्धृत नहीं करता है (यहाँ कोई समस्या नहीं है क्योंकि वैसे भी bash खाली कुंजी का समर्थन नहीं करता है)। इसके अलावा, जब एकल उद्धरण ( कुछ मूल्यों के लिए ${var@Q}रिसॉर्ट्स $'...') के अलावा अन्य शैलियों का उपयोग करते हुए यह महत्वपूर्ण है कि कोड एक ही स्थान पर प्रबलित हो।
स्टीफन चेज़लस

@ स्टीफनचेज़लस, मुझे लगता है कि आप एक परेशान मूल्य का मतलब है, एक खाली स्ट्रिंग नहीं है? ( x=; echo "${x@Q}"देता है '', unset x; echo "${x@Q}"कुछ भी नहीं देता है।) बैश के @Qपसंद करते हैं लगता है $'\n'एक शाब्दिक न्यू लाइन, जो वास्तव में कुछ स्थितियों में अच्छा किया जा सकता है अधिक (लेकिन मैं नहीं बता सकता कि दूसरों पसंद करते हैं)। बेशक एक विकल्प होने बुरा नहीं होगा।
इलकाचू

अरे हाँ क्षमा करें, मुझे इस बात का एहसास नहीं था। यह तो mksh से एक अंतर है। $'...'वाक्य रचना बातों में एक संभावित समस्या की तरह है LC_ALL=zh_HK.big5hkscs bash -c 'a=$'\''\n\u3b1'\''; printf "%s\n" "${a@Q}"'जो आउटपुट $'\n<0xa3><0x5c>'और 0x5cअकेले बैकस्लैश है ताकि आप एक समस्या है चाहते हैं, तो यह है कि बोली के लिए एक अलग स्थान में व्याख्या की गई।
स्टीफन चेज़लस

10
declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'

2 कांटा

शायद यह:

printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1

printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb

printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2                              2
a1                              1
f50                             abcd
zz                              Hello World
b1                              bbb

3 कांटे

या यह:

paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

कोई कांटा नहीं

से तुलना की जाए

for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

निष्पादन समय की तुलना

अंतिम सिंटैक्स कांटा का उपयोग नहीं करते हैं, वे जल्दी हो सकता है:

time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
      5      11      76
real    0m0.005s
user    0m0.000s
sys     0m0.000s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
      5       6      41
real    0m0.008s
user    0m0.000s
sys     0m0.000s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
      5       6      41
real    0m0.002s
user    0m0.000s
sys     0m0.001s

लेकिन यह पुष्टि सही नहीं रहती है यदि सरणी बड़ी हो जाती है; यदि कांटे कम करना छोटी प्रक्रिया के लिए कुशल है, तो समर्पित उपकरण का उपयोग बड़ी प्रक्रिया के लिए अधिक कुशल है।

for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done


time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
  17581   35163  292941
real    0m0.150s
user    0m0.124s
sys     0m0.036s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
  17581   17582  169875
real    0m0.140s
user    0m0.000s
sys     0m0.004s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
  17581   17582  169875
real    0m0.312s
user    0m0.268s
sys     0m0.076s

टिप्पणी

जैसा कि दोनों ( forked ) समाधान संरेखण का उपयोग करते हैं , उनमें से कोई भी काम नहीं करेगा यदि किसी भी चर में एक नई रेखा होती है । इस मामले में, एकमात्र तरीका एक forलूप है।


चतुर दिखने के दौरान, दोनों तरीके ए की तुलना में कम कुशल हैं for। जो वाकई शर्म की बात है।
सातु कटसरा

@SatoKatsura मैं सहमत हूं, लेकिन अगर धीमी गति से, वाक्यविन्यास का उपयोग prकम है ... मुझे prवाक्यविन्यास के बारे में निश्चित नहीं है , यहां तक ​​कि बड़े सरणियों के साथ भी!
एफ। होरी

2
@MiniMax क्योंकि यह सही परिणाम (समान तत्व, गलत क्रम) का उत्पादन नहीं करता है। आपको काम करने के लिए एरेज़ ${!array[@]}और ${array[@]}पहली बार ज़िप करने की आवश्यकता होगी ।
सातु कटसरा

1
यही कारण है कि पिछले स्निपेट के साथ pasteहै अब की तुलना में forसवाल एक पंक्ति पर लिखा में पाश for i in "${!array[@]}"; do echo "$i=${array[$i]}" ; done, लेकिन दो subshells और एक बाहरी कार्यक्रम की आवश्यकता है। वह नटखट कैसे है? prयदि कई तत्व हैं, तो इसका समाधान भी टूट जाता है, क्योंकि यह आउटपुट को शांत करने की कोशिश करता है। आपको कुछ ऐसा उपयोग करने की आवश्यकता होगी, | pr -2t -l"${#array[@]}"जो सरल लूप की तुलना में याद रखना मुश्किल हो रहा है, और फिर से, इससे अधिक लंबा है।
इलकाचू

1
में bash, का cmd1 | cmd2अर्थ है 2 कांटे, भले ही cmd1 या cmd2 या दोनों बिल्ट हैं।
स्टीफन चेज़लस

2

यदि आप बेहतर साहचर्य सरणी समर्थन के साथ एक खोल की तलाश कर रहे हैं, तो प्रयास करें zsh

zsh1998 में (kash93 और 2009 के लिए बैश के लिए) की तुलना में 1998 में (जहां साहचर्य सरणियों को जोड़ा गया था), $varया (गैर-खाली) कुंजी (उसी क्रम में) के लिए, हैश ${(v)var}के (गैर-खाली) मूल्यों का विस्तार होता है ${(k)var}, और ${(kv)var}कुंजी और मान दोनों के लिए।

रिक्त मानों को सुरक्षित रखने के लिए, सरणियों के लिए, आपको @ध्वज का उद्धरण और उपयोग करने की आवश्यकता है ।

तो कुंजी और मूल्यों को मुद्रित करने के लिए, यह सिर्फ एक बात है

printf '%s => %s\n' "${(@kv)var}"

हालांकि संभवतः खाली हैश के लिए खाते में, आपको करना चाहिए:

(($#var)) &&  printf '%s => %s\n' "${(@kv)var}"

यह भी ध्यान दें कि zsh ksh93's (प्रतिलिपि द्वारा bash) की तुलना में बहुत अधिक समझदार और उपयोगी सरणी वाक्य विन्यास का उपयोग करता है :

typeset -A var
var=(k1 v1 k2 v2 '' empty '*' star)

इससे साहचर्य सरणियों को कॉपी या मर्ज करना बहुत आसान हो जाता है:

var2=("${(@kv)var1}")
var3+=("${(@kv)var2}")
var4=("${@kv)var4}" "${(@kv)var5}")

(आप लूप के बिना हैश को आसानी से कॉपी नहीं कर सकते हैं bash, और ध्यान दें कि bashवर्तमान में NUL बाइट्स के साथ खाली कुंजी या कुंजी / मान का समर्थन नहीं करता है)।

यह भी देखें zshकि आप आमतौर पर साहचर्य सरणियों के साथ काम करने की आवश्यकता होगी zipping सुविधाएँ:

keys=($(<keys.txt)) values=($(<values.txt))
hash=(${keys:^values})

1

चूंकि टाइपसेट वही करता है जो आप चाहते हैं कि सिर्फ इसके आउटपुट को क्यों न संपादित किया जाए?

typeset -p array | sed s/^.*\(// | tr -d ")\'\""  | tr "[" "\n" | sed s/]=/' = '/

देता है

a2 = 2  
a1 = 1  
b1 = bbb 

कहाँ पे

array='([a2]="2" [a1]="1" [b1]="bbb" )'

Verbose लेकिन यह देखना बहुत आसान है कि स्वरूपण कैसे काम करता है: बस sed और tr कमांड के उत्तरोत्तर अधिक से पाइपलाइन को निष्पादित करें । सुंदर मुद्रण स्वाद के अनुरूप उन्हें संशोधित करें।


इस तरह की पाइपलाइन उस क्षण को विफल करने के लिए बाध्य है जब सरणी के कुछ कुंजी या मानों में आपके द्वारा प्रतिस्थापित किए जा रहे पात्रों में से कोई भी हो, जैसे कोष्ठक, कोष्ठक या उद्धरण। और की एक पाइप लाइन sedऔर trएक से भी बहुत सरल नहीं है forके साथ पाश printf
इलकाचू

इसके अलावा, आप जानते हैं कि trचरित्र-दर-वर्ण अनुवाद करें, यह तार से मेल नहीं खाता है? tr "]=" " ="""] एक स्थान पर और =एक =, स्थिति की परवाह किए बिना। तो आप शायद सभी तीनों trको एक में जोड़ सकते हैं ।
इलकाचू

कुछ गैर-अल्फ़ान्यूमेरिक वर्णों के बारे में बहुत सच है जो इसे गम करते हैं। हालाँकि, कुछ भी करने के लिए उन्हें परिमाण का एक आदेश अधिक जटिल और कम पठनीय मिलता है, जब तक कि आपके डेटा फीड में उनके पास होने का वास्तव में अच्छा कारण नहीं है और इस सवाल में कहा गया है कि मुझे लगता है कि वे हमारे यहां पहुंचने से पहले फ़िल्टर कर चुके हैं। हमेशा आपका स्पष्ट कैवेट होना चाहिए। मैं इन पाइपलाइनों को सरल और उदाहरण के लिए डिबगिंग उद्देश्यों के लिए खोजता हूं, एक प्रिंटफ ग्लोब की तुलना में जो या तो पूरी तरह से काम करता है या आपके चेहरे पर उड़ता है। यहां आप प्रति तत्व एक सरल परिवर्तन करते हैं, इसका परीक्षण करते हैं, फिर 1 और जोड़ते हैं।
नादरेक

मेरी गलती! मेरे _tr_s और _sed_s को पूरी तरह से मिला दिया है! नवीनतम संपादन में फिक्स्ड।
नाद्रेक

1

एक और विकल्प यह है कि आप जो चाहते हैं उसके लिए सभी चर और grep को सूचीबद्ध करें।

set | grep -e '^aa='

मैं डिबगिंग के लिए इसका उपयोग करता हूं। मुझे संदेह है कि यह बहुत अच्छा है क्योंकि यह सभी चर को सूचीबद्ध करता है।

यदि आप अक्सर ऐसा कर रहे थे, तो आप इसे इस तरह से बना सकते हैं:

aap() { set | grep -e "^$1="; }

दुर्भाग्य से जब हम समय का उपयोग करके प्रदर्शन की जाँच करते हैं:

$ time aap aa aa=([0]="abc") . real 0m0.014s user 0m0.003s sys 0m0.006s

इसलिए, यदि आप ऐसा बहुत बार कर रहे हैं, तो आप @ F.Hauri का कोई FORKS संस्करण नहीं चाहेंगे क्योंकि यह बहुत तेज़ है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.