अगर मेरे पास बाश में इस तरह की एक सरणी है:
FOO=( a b c )
मैं तत्वों को कॉमा के साथ कैसे जोड़ूं? उदाहरण के लिए, उत्पादन a,b,c
।
अगर मेरे पास बाश में इस तरह की एक सरणी है:
FOO=( a b c )
मैं तत्वों को कॉमा के साथ कैसे जोड़ूं? उदाहरण के लिए, उत्पादन a,b,c
।
जवाबों:
100% शुद्ध बैश (कोई बाहरी आदेश) में एक समारोह के रूप में पास्कल पिल्ज़ द्वारा पुनर्लेखन समाधान:
function join_by { local IFS="$1"; shift; echo "$*"; }
उदाहरण के लिए,
join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c
वैकल्पिक रूप से, हम मल्टी-कैरेक्टर सीमांकक का समर्थन करने के लिए प्रिंट का उपयोग कर सकते हैं, @gniourf_gniourf द्वारा विचार का उपयोग कर
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
उदाहरण के लिए,
join_by , a b c #a,b,c
join_by ' , ' a b c #a , b , c
join_by ')|(' a b c #a)|(b)|(c
join_by ' %s ' a b c #a %s b %s c
join_by $'\n' a b c #a<newline>b<newline>c
join_by - a b c #a-b-c
join_by '\' a b c #a\b\c
konsolebox
शैली का उपयोग करें :) function join { local IFS=$1; __="${*:2}"; }
या function join { IFS=$1 eval '__="${*:2}"'; }
। इसके __
बाद उपयोग करें । हां, मैं __
एक परिणाम चर के रूप में उपयोग को बढ़ावा देने वाला हूं ;) (और एक सामान्य पुनरावृत्ति चर या अस्थायी चर)। यदि अवधारणा एक लोकप्रिय बैश विकी साइट पर मिलती है, तो उन्होंने मुझे :) :)
$d
को प्रारूप प्रारूप में न रखें printf
। आपको लगता है कि आप "बच गए" से सुरक्षित हैं, %
लेकिन अन्य चेतावनी भी हैं: जब सीमांकक में एक बैकस्लैश (जैसे \n
) या तब होता है जब सीमांकक एक हाइफ़न के साथ शुरू होता है (और शायद अन्य मैं अब सोच भी नहीं सकता)। आप निश्चित रूप से इन्हें ठीक कर सकते हैं (बैकस्लैश को डबल बैकस्लैश और उपयोग द्वारा प्रतिस्थापित करें printf -- "$d%s"
), लेकिन कुछ बिंदु पर आपको लगेगा कि आप इसके साथ काम करने के बजाय शेल के खिलाफ लड़ रहे हैं। इसीलिए, नीचे दिए गए मेरे उत्तर में, मैंने परिसीमन में शामिल होने की शर्तों को पूर्व निर्धारित किया।
अभी तक एक और समाधान:
#!/bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
bar=$(printf ",%s" "${foo[@]}")
bar=${bar:1}
echo $bar
संपादित करें: लेकिन बहु-वर्ण चर लंबाई विभाजक के लिए समान:
#!/bin/bash
separator=")|(" # e.g. constructing regex, pray it does not contain %s
foo=('foo bar' 'foo baz' 'bar baz')
regex="$( printf "${separator}%s" "${foo[@]}" )"
regex="${regex:${#separator}}" # remove leading separator
echo "${regex}"
# Prints: foo bar)|(foo baz)|(bar baz
printf -v bar ",%s" "${foo[@]}"
? यह एक fork
कम (वास्तव में clone
) है। यह भी एक फ़ाइल को पढ़ने forking है: printf -v bar ",%s" $(<infile)
।
$separator
इसमें %s
या ऐसा नहीं है , आप अपनी printf
ताकत बना सकते हैं printf "%s%s" "$separator" "${foo[@]}"
:।
printf "%s%s"
उपयोग करना, पहली बार आउटपुट के पहले सेट में विभाजक का उपयोग करेगा, और उसके बाद केवल शेष तर्क को
printf "%s" "${foo[@]/#/$separator}"
।
IFS=; regex="${foo[*]/#/$separator}"
। इस बिंदु पर यह अनिवार्य रूप से gniourf_gniourf का उत्तर बन जाता है जो IMO शुरू से ही क्लीनर है, अर्थात IFS परिवर्तनों और अस्थायी vars के दायरे को सीमित करने के लिए फ़ंक्शन का उपयोग करता है।
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
bar=$( IFS=, ; echo "${foo[*]}" )
@
बजाय का उपयोग कर *
, के रूप में $(IFS=, ; echo "${foo[@]}")
? मैं देख सकता हूं कि *
पहले से ही तत्वों में व्हाट्सएप को संरक्षित करता है, फिर से सुनिश्चित नहीं है कि, चूंकि @
आमतौर पर इस खातिर आवश्यक है।
*
। बैश मैन पेज में, "स्पेशल पैरामीटर्स" की खोज करें और आगे के स्पष्टीकरण की तलाश करें *
:
शायद, जैसे,
SAVE_IFS="$IFS"
IFS=","
FOOJOIN="${FOO[*]}"
IFS="$SAVE_IFS"
echo "$FOOJOIN"
echo "-${IFS}-"
(घुंघराले ब्रेसिज़ को वैरिएबल नाम से अलग करते हैं)।
echo $IFS
एक ही काम करता है।
आश्चर्यजनक रूप से मेरा समाधान अभी तक नहीं दिया गया है :) यह मेरे लिए सबसे सरल तरीका है। यह एक समारोह की जरूरत नहीं है:
IFS=, eval 'joined="${foo[*]}"'
नोट: यह समाधान गैर-पॉसिक्स मोड में अच्छी तरह से काम करने के लिए देखा गया था। में POSIX मोड , तत्व अभी भी ठीक से शामिल हुए, लेकिन कर रहे हैं IFS=,
स्थायी हो जाता है।
यहां 100% शुद्ध बैश फ़ंक्शन है जो काम करता है:
join() {
# $1 is return variable name
# $2 is sep
# $3... are the elements to join
local retname=$1 sep=$2 ret=$3
shift 3 || shift $(($#))
printf -v "$retname" "%s" "$ret${@/#/$sep}"
}
देखो:
$ a=( one two "three three" four five )
$ join joineda " and " "${a[@]}"
$ echo "$joineda"
one and two and three three and four and five
$ join joinedb randomsep "only one element"
$ echo "$joinedb"
only one element
$ join joinedc randomsep
$ echo "$joinedc"
$ a=( $' stuff with\nnewlines\n' $'and trailing newlines\n\n' )
$ join joineda $'a sep with\nnewlines\n' "${a[@]}"
$ echo "$joineda"
stuff with
newlines
a sep with
newlines
and trailing newlines
$
यह भी अनुगामी newlines को संरक्षित करता है, और फ़ंक्शन के परिणाम प्राप्त करने के लिए एक उप-संस्करण की आवश्यकता नहीं है। यदि आपको कोई पसंद नहीं है printf -v
(आप इसे क्यों पसंद नहीं करेंगे?) और एक चर नाम से गुजर रहा है, तो आप निश्चित रूप से लौटे हुए के लिए एक वैश्विक चर का उपयोग कर सकते हैं:
join() {
# $1 is sep
# $2... are the elements to join
# return is in global variable join_ret
local sep=$1 IFS=
join_ret=$2
shift 2 || shift $(($#))
join_ret+="${*/#/$sep}"
}
join_ret
एक स्थानीय चर बनाकर क्लीनर बनाया जा सकता है , और फिर इसे अंत में गूंज सकता है। यह सामान्य शेल स्क्रिप्टिंग तरीके से शामिल होने () में शामिल होने की अनुमति देता है, उदाहरण के लिए $(join ":" one two three)
, और वैश्विक चर की आवश्यकता नहीं है।
$(...)
ट्रिमिंग नईलाइन्स; इसलिए यदि सरणी के अंतिम क्षेत्र में नई अनुगामी शामिल हैं, तो इन्हें ट्रिम किया जाएगा (डेमो देखें जहां वे मेरे डिज़ाइन के साथ ट्रिम नहीं किए गए हैं)।
यह सभी मौजूदा समाधानों से बहुत अलग नहीं है, लेकिन यह एक अलग फ़ंक्शन का उपयोग करने से बचता है, IFS
मूल शेल में संशोधित नहीं होता है और सभी एक एकल में हैं:
arr=(a b c)
printf '%s\n' "$(IFS=,; printf '%s' "${arr[*]}")"
जिसके परिणामस्वरूप
a,b,c
सीमा: विभाजक एक वर्ण से अधिक लंबा नहीं हो सकता है।
कोई बाहरी आदेश का उपयोग करना:
$ FOO=( a b c ) # initialize the array
$ BAR=${FOO[@]} # create a space delimited string from array
$ BAZ=${BAR// /,} # use parameter expansion to substitute spaces with comma
$ echo $BAZ
a,b,c
चेतावनी, यह मानता है कि तत्वों में व्हाट्सएप नहीं है।
echo ${FOO[@]} | tr ' ' ','
मैं एक स्ट्रिंग के रूप में सरणी को प्रतिध्वनित करूंगा, फिर रिक्त स्थान को लाइन फीड में रूपांतरित करूंगा, और फिर paste
सब कुछ एक पंक्ति में शामिल करने के लिए उपयोग करूंगा जैसे:
tr " " "\n" <<< "$FOO" | paste -sd , -
परिणाम:
a,b,c
यह मुझे सबसे तेज और सबसे साफ लगता है!
$FOO
हालांकि, सरणी का पहला तत्व है। इसके अलावा, यह रिक्त स्थान वाले सरणी तत्वों के लिए टूटता है।
@ के पुन: उपयोग से 'कोई फर्क नहीं पड़ता' समाधान, लेकिन एक बयान के साथ $ {: 1} सबस्टेशन और इंटरमीडिएट चर की जरूरत से बचना चाहिए।
echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )
प्रिंटफ में 'प्रारूप स्ट्रिंग का उपयोग तर्कों को संतुष्ट करने के लिए अक्सर आवश्यक रूप से किया जाता है।' इसके मैन पेजों में, ताकि स्ट्रिंग्स के कॉन्टैक्शंस को प्रलेखित किया जाए। फिर ट्रिक अंतिम स्पिंटर को काटने के लिए LIST की लंबाई का उपयोग करने के लिए है, क्योंकि कट केवल फ़ील्ड की गिनती के दौरान LIST के लेन को बनाए रखेगा।
s=$(IFS=, eval 'echo "${FOO[*]}"')
@Q
से गलत तरीके से जुड़ने से बच सकते हैं, जब वे उनके साथ foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"
'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
प्रिंटफ़ समाधान जो किसी भी लम्बाई के विभाजकों को स्वीकार करता है (@ के आधार पर जवाब नहीं देता है)
#/!bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
sep=',' # can be of any length
bar=$(printf "${sep}%s" "${foo[@]}")
bar=${bar:${#sep}}
echo $bar
printf
प्रारूप निर्दिष्ट करने वाला (जैसे। %s
अनायास $sep
ही समस्याओं का कारण होगा।
sep
के साथ सैनिटाइज किया जा सकता है ${sep//\%/%%}
। मुझे आपका समाधान ( ${bar#${sep}}
या ${bar%${sep}}
वैकल्पिक) से बेहतर लगता है । यह अच्छा है अगर एक फ़ंक्शन में परिवर्तित किया जाता है जो स्टोर का परिणाम जेनेरिक चर की तरह होता है __
, और echo
यह नहीं ।
function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
$ set a 'b c' d
$ history -p "$@" | paste -sd,
a,b c,d
HISTSIZE=0
?
paste -sd,
इतिहास के उपयोग के बारे में नहीं है।
HISTSIZE=0
- इसे आज़माएं।
शीर्ष उत्तर का छोटा संस्करण:
joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }
उपयोग:
joinStrings "$myDelimiter" "${myArray[@]}"
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; }
यह उपयोग के साथ काम करता है: join_strings 'delim' "${array[@]}"
या join_strings 'delim' ${array[@]}
निम्नलिखित विचार के साथ अब तक की सभी दुनियाओं में सर्वश्रेष्ठ का मिश्रण करें।
# join with separator
join_ws() { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }
यह छोटी कृति है
उदाहरण:
$ join_ws , a b c
a,b,c
$ join_ws '' a b c
abc
$ join_ws $'\n' a b c
a
b
c
$ join_ws ' \/ ' A B C
A \/ B \/ C
join_ws ,
(बिना किसी तर्क के) गलत तरीके से आउटपुट ,,
। 2. join_ws , -e
गलत तरीके से आउटपुट कुछ भी नहीं (कि है, क्योंकि आप गलत तरीके से उपयोग कर रहे हैं echo
के बजाय printf
)। मुझे वास्तव में नहीं पता है कि आपने echo
इसके बजाय विज्ञापन का उपयोग क्यों किया printf
: echo
कुख्यात रूप से टूटा हुआ है, और printf
एक मजबूत बिलिन है।
अभी मैं उपयोग कर रहा हूं:
TO_IGNORE=(
E201 # Whitespace after '('
E301 # Expected N blank lines, found M
E303 # Too many blank lines (pep8 gets confused by comments)
)
ARGS="--ignore `echo ${TO_IGNORE[@]} | tr ' ' ','`"
जो काम करता है, लेकिन (सामान्य स्थिति में) बहुत बुरी तरह से टूट जाएगा अगर सरणी तत्वों में उनके लिए जगह है।
(रुचि रखने वालों के लिए, यह pep8.py के आसपास एक आवरण स्क्रिप्ट है )
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
:। ऑपरेटर $()
बैकटिक्स की तुलना में अधिक शक्तिशाली है (घोंसले के शिकार $()
और अनुमति देता है ""
)। ${TO_IGNORE[@]}
दोहरे उद्धरण चिह्नों के साथ लपेटकर भी मदद करनी चाहिए।
मल्टीचैकर विभाजकों के लिए पर्ल का उपयोग करें:
function join {
perl -e '$s = shift @ARGV; print join($s, @ARGV);' "$@";
}
join ', ' a b c # a, b, c
या एक पंक्ति में:
perl -le 'print join(shift, @ARGV);' ', ' 1 2 3
1, 2, 3
join
नाम कुछ बकवास के साथ संघर्ष करता है OS X
.. मैं इसे कॉल करूँगा conjoined
, या शायद jackie_joyner_kersee
?
मेरी अब तक की सर्वश्रेष्ठ दुनिया के संयोजन पर विस्तृत टिप्पणियों के लिए @gniourf_gniourf धन्यवाद। कोड को पूरी तरह से डिजाइन और परीक्षण नहीं करने के लिए क्षमा करें। यहाँ एक बेहतर कोशिश है।
# join with separator
join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }
गर्भाधान द्वारा यह सौंदर्य है
अतिरिक्त उदाहरण:
$ join_ws '' a b c
abc
$ join_ws ':' {1,7}{A..C}
1A:1B:1C:7A:7B:7C
$ join_ws -e -e
-e
$ join_ws $'\033[F' $'\n\n\n' 1. 2. 3. $'\n\n\n\n'
3.
2.
1.
$ join_ws $
$
यदि आप जिन तत्वों से जुड़ना चाहते हैं, वह एक ऐसा स्थान नहीं है जो केवल एक अलग की गई स्ट्रिंग है, तो आप कुछ ऐसा कर सकते हैं:
foo="aa bb cc dd"
bar=`for i in $foo; do printf ",'%s'" $i; done`
bar=${bar:1}
echo $bar
'aa','bb','cc','dd'
उदाहरण के लिए, मेरा उपयोग मामला यह है कि कुछ तार मेरी शेल स्क्रिप्ट में पास किए गए हैं और मुझे SQL क्वेरी पर चलने के लिए इसका उपयोग करने की आवश्यकता है:
./my_script "aa bb cc dd"
My_script में, मुझे "SELECT * FROM टेबल से करना है, जहां IN ('आ', 'bb', 'cc', 'dd') करने की जरूरत है। फिर उपरोक्त कमांड उपयोगी होगी।
printf -v bar ...
प्रिंटफ़ लूप को एक सबशेल में चलाने और आउटपुट कैप्चर करने के बजाय उपयोग कर सकते हैं ।
यहाँ एक है कि अधिकांश POSIX संगत गोले समर्थन करते हैं:
join_by() {
# Usage: join_by "||" a b c d
local arg arr=() sep="$1"
shift
for arg in "$@"; do
if [ 0 -lt "${#arr[@]}" ]; then
arr+=("${sep}")
fi
arr+=("${arg}") || break
done
printf "%s" "${arr[@]}"
}
किसी सरणी में सीधे संदर्भित करने के लिए चर अप्रत्यक्षता का उपयोग करना भी काम करता है। नामांकित संदर्भों का भी उपयोग किया जा सकता है, लेकिन वे केवल 4.3 में उपलब्ध हो गए।
एक फ़ंक्शन के इस रूप का उपयोग करने का लाभ यह है कि आपके पास विभाजक वैकल्पिक हो सकता है (डिफ़ॉल्ट के पहले चरित्र के लिए डिफ़ॉल्ट IFS
, जो कि एक स्थान है; शायद इसे एक खाली स्ट्रिंग बनाएं यदि आप चाहें), और यह दो बार विस्तार मान से बचा जाता है (पहले जब मापदंडों के रूप में पारित किया जाता है, और "$@"
फ़ंक्शन के अंदर दूसरा )।
इस समाधान के लिए उपयोगकर्ता को कमांड प्रतिस्थापन के अंदर फ़ंक्शन को कॉल करने की आवश्यकता नहीं होती है - जो एक सबमिशन को समन करता है, एक स्ट्रिंग का एक सम्मिलित संस्करण दूसरे चर को सौंपा जाता है।
function join_by_ref {
__=
local __r=$1[@] __s=${2-' '}
printf -v __ "${__s//\%/%%}%s" "${!__r}"
__=${__:${#__s}}
}
array=(1 2 3 4)
join_by_ref array
echo "$__" # Prints '1 2 3 4'.
join_by_ref array '%s'
echo "$__" # Prints '1%s2%s3%s4'.
join_by_ref 'invalid*' '%s' # Bash 4.4 shows "invalid*[@]: bad substitution".
echo "$__" # Prints nothing but newline.
फंक्शन के लिए अधिक आरामदायक नाम का उपयोग करने के लिए स्वतंत्र महसूस करें।
यह 3.1 से 5.0-अल्फा से काम करता है। जैसा कि देखा गया है, चर अप्रत्यक्ष केवल चर के साथ ही नहीं बल्कि अन्य मापदंडों के साथ भी काम करता है।
एक पैरामीटर एक इकाई है जो मूल्यों को संग्रहीत करता है। यह एक नाम, एक संख्या, या विशेष पैरामीटर के तहत नीचे सूचीबद्ध विशेष वर्णों में से एक हो सकता है। एक चर एक नाम से चिह्नित एक पैरामीटर है।
सरणी और सरणी तत्व भी पैरामीटर (ऐसी संस्थाएं हैं जो मूल्य को संग्रहीत करती हैं), और सरणियों के संदर्भ तकनीकी रूप से भी मापदंडों के संदर्भ हैं। और विशेष पैरामीटर की तरह @
, array[@]
एक वैध संदर्भ भी बनाता है।
विस्तारित या चयनात्मक रूप विस्तार (जैसे विस्तारित विस्तार) जो पैरामीटर से विचलन को संदर्भित करता है वह अब काम नहीं करता है।
बैश 5.0 के रिलीज़ संस्करण में, चर अप्रत्यक्ष को पहले से ही अप्रत्यक्ष विस्तार कहा जाता है और इसका व्यवहार पहले से ही मैनुअल में स्पष्ट रूप से प्रलेखित है:
यदि पैरामीटर का पहला वर्ण विस्मयादिबोधक बिंदु (!) है, और पैरामीटर एक nameref नहीं है, तो यह अप्रत्यक्ष स्तर का परिचय देता है। बैश नए पैरामीटर के रूप में बाकी पैरामीटर का विस्तार करके गठित मूल्य का उपयोग करता है; इसके बाद इसका विस्तार किया जाता है और मूल पैरामीटर के विस्तार के बजाय शेष विस्तार में इसका उपयोग किया जाता है। इसे अप्रत्यक्ष विस्तार के रूप में जाना जाता है।
ध्यान दें कि के दस्तावेज में ले रहा है ${parameter}
, parameter
के रूप में जाना जाता है "वर्णित (में) के रूप में एक खोल पैरामीटर पैरामीटर या एक सरणी संदर्भ "। और सरणियों के दस्तावेज़ीकरण में, यह उल्लेख किया गया है कि "सरणी के किसी भी तत्व का उपयोग करके संदर्भित किया जा सकता है ${name[subscript]}
"। यह __r[@]
एक सरणी संदर्भ बनाता है ।
रिकार्डो गली के जवाब में मेरी टिप्पणी देखें ।
__
चर नाम के रूप में उपयोग करने का कोई विशेष कारण है ? कोड को वास्तव में अपठनीय बनाता है।
यदि आप एक लूप में सरणी बनाते हैं, तो यहां एक सरल तरीका है:
arr=()
for x in $(some_cmd); do
arr+=($x,)
done
arr[-1]=${arr[-1]%,}
echo ${arr[*]}
x=${"${arr[*]}"// /,}
यह इसे करने का सबसे छोटा तरीका है।
उदाहरण,
arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x # output: 1,2,3,4,5
bash: ${"${arr[*]}"// /,}: bad substitution
शायद मुझे कुछ स्पष्ट याद आ रहा है, क्योंकि मैं पूरी मार / ज़श चीज़ के लिए एक नया हूँ, लेकिन यह मुझे ऐसा लगता है जैसे आपको बिल्कुल भी उपयोग करने की आवश्यकता नहीं है printf
। और न ही यह वास्तव में बिना करने के लिए बदसूरत मिलता है।
join() {
separator=$1
arr=$*
arr=${arr:2} # throw away separator and following space
arr=${arr// /$separator}
}
कम से कम, इसने मेरे लिए इस तरह से बिना मुद्दे के काम किया है।
उदाहरण के लिए, join \| *.sh
जो, मान लें कि मैं अपनी ~
निर्देशिका में हूँ , आउटपुट utilities.sh|play.sh|foobar.sh
। मेरे लिए काफी अच्छा।
EDIT: यह मूल रूप से Nil Geisweiller का उत्तर है , लेकिन एक फ़ंक्शन में सामान्यीकृत है।
liststr=""
for item in list
do
liststr=$item,$liststr
done
LEN=`expr length $liststr`
LEN=`expr $LEN - 1`
liststr=${liststr:0:$LEN}
यह अंत में अतिरिक्त अल्पविराम का भी ख्याल रखता है। मैं कोई बैश विशेषज्ञ नहीं हूं। बस मेरा 2 सी, क्योंकि यह अधिक प्राथमिक और समझने योग्य है