तत्वों में रिक्त स्थान के साथ बैश सरणी


150

मैं अपने कैमरे से फ़ाइल नाम के बैश में एक सरणी बनाने की कोशिश कर रहा हूँ:

FILES=(2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg)

जैसा कि आप देख सकते हैं, प्रत्येक फ़ाइल नाम के बीच में एक स्थान है।

मैंने प्रत्येक नाम को उद्धरणों में लपेटने की कोशिश की है, और बैकस्लैश के साथ अंतरिक्ष से बचकर, जिनमें से कोई भी काम नहीं करता है।

जब मैं एरे तत्वों को एक्सेस करने की कोशिश करता हूं, तो वह स्पेस को एलिमेलिमिटर के रूप में मानता है।

मैं नाम के अंदर की जगह के साथ फ़ाइल नाम कैसे ठीक से पकड़ सकता हूं?


क्या आपने पुराने तरीके से फाइलों को जोड़ने की कोशिश की है? पसंद है FILES[0] = ...? (संपादित करें: मैंने अभी-अभी काम नहीं किया है। दिलचस्प है)।
डेन फागो


यहाँ जवाब के सभी मेरे लिए Cygwin का उपयोग कर टूट जाते हैं। यह अजीब चीजें करता है अगर फ़ाइल नाम, अवधि में रिक्त स्थान हैं। मैं उन सभी तत्वों की टेक्स्ट फ़ाइल लिस्टिंग में "सरणी" बनाकर इसके चारों ओर काम करता हूं, जिनके साथ मैं काम करना चाहता हूं, और फ़ाइल में लाइनों पर पुनरावृत्ति करना: स्वरूपण बैकस्टिक्स के साथ यहां कोष्ठक में कमांड के आसपास mucking है: IFS = "; सरणी = ( find . -maxdepth 1 -type f -iname \*.$1 -printf '%f\n'); $ {सरणी [@]} में तत्व के लिए; गूंज $ तत्व; किया
एलेक्स हॉल

जवाबों:


121

मुझे लगता है कि समस्या आंशिक रूप से हो सकती है कि आप तत्वों को कैसे एक्सेस कर रहे हैं। यदि मैं एक सरल काम करता for elem in $FILESहूं, तो मैं आपके जैसे ही मुद्दे का अनुभव करता हूं। हालाँकि, अगर मैं इसके सूचकांकों के माध्यम से सरणी तक पहुँचता हूँ, जैसे कि, यह काम करता है अगर मैं तत्वों को संख्यात्मक रूप से या पलायन के साथ जोड़ता हूं:

for ((i = 0; i < ${#FILES[@]}; i++))
do
    echo "${FILES[$i]}"
done

इनमें से किसी भी घोषणा $FILESको काम करना चाहिए:

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

या

FILES=("2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg")

या

FILES[0]="2011-09-04 21.43.02.jpg"
FILES[1]="2011-09-05 10.23.14.jpg"
FILES[2]="2011-09-09 12.31.16.jpg"
FILES[3]="2011-09-11 08.43.12.jpg"

6
ध्यान दें कि जब आप सरणी तत्वों (जैसे echo "${FILES[$i]}") का उपयोग करते हैं तो आपको दोहरे उद्धरणों का उपयोग करना चाहिए । इसके लिए कोई फर्क नहीं पड़ता है echo, लेकिन यह किसी भी चीज़ के लिए होगा जो इसे फ़ाइल नाम के रूप में उपयोग करता है।
गॉर्डन डेविसन

26
जब आप तत्वों के साथ लूप कर सकते हैं, तो अनुक्रमित पर लूप करना आवश्यक नहीं है for f in "${FILES[@]}"
मार्क एडगर

10
@MarkEdgar मैं $ {FILES [@]} में च के लिए समस्याओं का सामना कर रहा हूं जब सरणी सदस्यों के पास स्थान होते हैं। ऐसा लगता है कि रिक्त स्थान की फिर से व्याख्या की गई है, जिसमें रिक्त स्थान आपके मौजूदा सदस्यों को दो या अधिक तत्वों में थूक रहा है। ऐसा लगता है कि "" बहुत महत्वपूर्ण हैं
माइकल शॉ

1
स्टेटमेंट #में तेज ( ) सिंबल क्या करता है for ((i = 0; i < ${#FILES[@]}; i++))?
मीकल वियन

4
मैं इस छह साल पहले दिए लेकिन मेरा मानना है कि इसे पाने के लिए है गिनती सरणी फ़ाइलें में तत्वों की संख्या की।
दान फेगो

91

सरणी के आइटम तक पहुंचने के तरीके में कुछ गड़बड़ होनी चाहिए। यहां बताया गया है कि यह कैसे किया जाता है:

for elem in "${files[@]}"
...

से बैश मैनपेज :

किसी सरणी के किसी भी तत्व को $ {name [सबस्क्रिप्ट]} का उपयोग करके संदर्भित किया जा सकता है। ... यदि सबस्क्रिप्ट @ या * है, तो शब्द नाम के सभी सदस्यों तक विस्तृत है। ये सदस्यता केवल तभी भिन्न होती है जब शब्द दोहरे उद्धरण चिह्नों में प्रकट होता है। यदि यह शब्द दोहरा-उद्धृत है, तो $ {name [*]} IFS विशेष चर के पहले वर्ण द्वारा अलग किए गए प्रत्येक सरणी सदस्य के मान के साथ एक शब्द तक फैलता है, और $ {name [@]} प्रत्येक तत्व का विस्तार करता है एक अलग शब्द का नाम

बेशक, आपको एकल सदस्य का उपयोग करते समय दोहरे उद्धरण चिह्नों का उपयोग करना चाहिए

cp "${files[0]}" /tmp

3
इस गुच्छा में सबसे साफ, सबसे सुरुचिपूर्ण समाधान, हालांकि यह पुन: पुनरावृत्त होना चाहिए कि सरणी में परिभाषित प्रत्येक तत्व को उद्धृत किया जाना चाहिए।
मैवरिक

जबकि डैन फेगो का जवाब प्रभावी है, तत्वों में रिक्त स्थान को संभालने के लिए यह अधिक मुहावरेदार तरीका है।
डैनियल झांग

3
अन्य प्रोग्रामिंग भाषाओं से आने वाले, उस अंश से शब्दावली को समझना वास्तव में कठिन है। इसके अलावा वाक्य रचना चकरा देने वाली है। यदि आप इसे थोड़ा और अधिक में ले जा सकते हैं तो मैं बहुत आभारी रहूंगा। विशेष रूप सेexpands to a single word with the value of each array member separated by the first character of the IFS special variable
CL22

1
हां, सहमत हैं कि दोहरे उद्धरण इसे हल कर रहे हैं और यह अन्य समाधानों से बेहतर है। आगे समझाने के लिए - अधिकांश अन्य को दोहरे उद्धरण चिह्नों की कमी है। आपके पास सही है: for elem in "${files[@]}"जबकि उनके पास है for elem in ${files[@]}- इसलिए रिक्त स्थान विस्तार को भ्रमित करते हैं और व्यक्तिगत शब्दों पर चलने की कोशिश करते हैं।
arntg

यह मेरे लिए macOS 10.14.4 में काम नहीं करता है, जो "GNU बैश, संस्करण 3.2.57 (1) -release (x86_64-apple-darwin18)" का उपयोग करता है। हो सकता है कि बग के पुराने संस्करण में एक बग?
मार्क रिबाऊ

43

आपको तत्व सीमांकक के रूप में स्थान को रोकने के लिए IFS का उपयोग करने की आवश्यकता है।

FILES=("2011-09-04 21.43.02.jpg"
       "2011-09-05 10.23.14.jpg"
       "2011-09-09 12.31.16.jpg"
       "2011-09-11 08.43.12.jpg")
IFS=""
for jpg in ${FILES[*]}
do
    echo "${jpg}"
done

यदि आप के आधार पर अलग करना चाहते हैं। तो बस IFS = "।" आशा है कि यह आपकी मदद करता है :)


3
सरणी असाइनमेंट से पहले मुझे IFS = "" को स्थानांतरित करना था लेकिन यह सही उत्तर है।
लूट

मैं जानकारी को पार्स करने के लिए कई सरणियों का उपयोग कर रहा हूं और उनमें से केवल एक में IFS = "" का प्रभाव पड़ेगा। एक बार जब मैं आईएफएस = "" का उपयोग करता हूं, तो अन्य सभी सरणियाँ तदनुसार पार्स करना बंद कर देती हैं। इस बारे में कोई संकेत?
पाउलो पेड्रोसो

पाउलो, यहां एक और जवाब देखें जो आपके मामले के लिए बेहतर हो सकता है: stackoverflow.com/a/9089186/1041319 । IFS = "" की कोशिश नहीं की है, और ऐसा लगता है कि यह इसे सुरुचिपूर्ण ढंग से हल करता है - लेकिन आपका उदाहरण दिखाता है कि कुछ मामलों में किसी का सामना क्यों हो सकता है। एक पंक्ति में IFS = "" सेट करना संभव हो सकता है, लेकिन यह अभी भी अन्य समाधान की तुलना में अधिक भ्रामक हो सकता है।
arntg

इसने मेरे लिए बैश पर भी काम किया। धन्यवाद @Khushneet मैं इसे आधे घंटे के लिए खोज रहा था ...
csonuryilmaz

महान, केवल इस पृष्ठ पर उत्तर दें जिसने काम किया। लेकिन मुझे IFS="" सरणी निर्माण से पहले भी स्थानांतरित करना पड़ा ।
pkamb

13

मैं दूसरों से सहमत हूं कि यह संभव है कि आप उन तत्वों तक कैसे पहुंचें जो समस्या है। सरणी असाइनमेंट में फ़ाइल नामों को उद्धृत करना सही है:

FILES=(
  "2011-09-04 21.43.02.jpg"
  "2011-09-05 10.23.14.jpg"
  "2011-09-09 12.31.16.jpg"
  "2011-09-11 08.43.12.jpg"
)

for f in "${FILES[@]}"
do
  echo "$f"
done

फार्म के किसी भी सरणी के आसपास दोहरे उद्धरण चिह्नों का उपयोग करना "${FILES[@]}" एलील एलीमेंट एलिमेंट में एक शब्द में विभाजित करता है। यह उससे परे कोई शब्द-विभाजन नहीं करता है।

उपयोग करने "${FILES[*]}"का एक विशेष अर्थ भी है, लेकिन यह $ IFS के पहले वर्ण के साथ सरणी तत्वों में शामिल हो जाता है, जिसके परिणामस्वरूप एक है शब्द होता है, जो कि शायद आप नहीं चाहते हैं।

नंगे ${array[@]}या ${array[*]}विषयों के उपयोग से आगे शब्द-विभाजन के लिए उस विस्तार का परिणाम होता है, इसलिए आप रिक्त स्थान (और कुछ भी) में विभाजित शब्दों के साथ समाप्त हो जाएंगे$IFS एक शब्द प्रति तत्व तत्व के बजाय ) ।

लूप के लिए सी-स्टाइल का उपयोग करना भी ठीक है और यदि आप उस पर स्पष्ट नहीं हैं तो शब्द-विभाजन के बारे में चिंता करने से बचते हैं:

for (( i = 0; i < ${#FILES[@]}; i++ ))
do
  echo "${FILES[$i]}"
done

3

भागने का काम करता है।

#!/bin/bash

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

echo ${FILES[0]}
echo ${FILES[1]}
echo ${FILES[2]}
echo ${FILES[3]}

आउटपुट:

$ ./test.sh
2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg

स्ट्रिंग्स को उद्धृत करना भी उसी आउटपुट का उत्पादन करता है।


3

यदि आपके पास आपका सरणी इस प्रकार है: #! / Bin / bash

Unix[0]='Debian'
Unix[1]="Red Hat"
Unix[2]='Ubuntu'
Unix[3]='Suse'

for i in $(echo ${Unix[@]});
    do echo $i;
done

आपको मिलेगा:

Debian
Red
Hat
Ubuntu
Suse

मुझे पता नहीं क्यों लेकिन लूप रिक्त स्थान को तोड़ता है और उन्हें एक व्यक्तिगत आइटम के रूप में डालता है, यहां तक ​​कि आप इसे उद्धरण के साथ घेरते हैं।

इसके चारों ओर पाने के लिए, एरे में तत्वों को कॉल करने के बजाय, आप इंडेक्स को कॉल करते हैं, जो उद्धरण में लिपटे पूर्ण स्ट्रिंग thats लेता है। इसे उद्धरणों में लपेटा जाना चाहिए!

#!/bin/bash

Unix[0]='Debian'
Unix[1]='Red Hat'
Unix[2]='Ubuntu'
Unix[3]='Suse'

for i in $(echo ${!Unix[@]});
    do echo ${Unix[$i]};
done

तब आपको मिलेगा:

Debian
Red Hat
Ubuntu
Suse

2

मूल प्रश्न के उद्धरण / बचने की समस्या का बिल्कुल जवाब नहीं, लेकिन शायद कुछ ऐसा है जो वास्तव में ऑप के लिए अधिक उपयोगी होगा:

unset FILES
for f in 2011-*.jpg; do FILES+=("$f"); done
echo "${FILES[@]}"

जहां निश्चित रूप से अभिव्यक्ति को विशिष्ट आवश्यकता के लिए अपनाना होगा (जैसे *.jpgसभी के लिए या 2001-09-11*.jpgकेवल एक निश्चित दिन की तस्वीरों के लिए)।


0

एक अन्य समाधान "लूप" के बजाय "लूप" के लिए उपयोग कर रहा है:

index=0
while [ ${index} -lt ${#Array[@]} ]
  do
     echo ${Array[${index}]}
     index=$(( $index + 1 ))
  done

0

यदि आप का उपयोग करने पर अटक नहीं कर रहे हैं bash, फ़ाइल नामों में रिक्त स्थान के विभिन्न हैंडलिंग मछली खोल के लाभों में से एक है । एक निर्देशिका पर विचार करें जिसमें दो फाइलें हैं: "एक b.txt" और "b c.txt"। यहां एक अन्य कमांड से उत्पन्न फाइलों की सूची को संसाधित करने का एक उचित अनुमान है bash, लेकिन आपके द्वारा अनुभव किए गए फ़ाइल नामों में रिक्त स्थान के कारण यह विफल रहता है:

# bash
$ for f in $(ls *.txt); { echo $f; }
a
b.txt
b
c.txt

के साथ fish, वाक्यविन्यास लगभग समान है, लेकिन परिणाम वही है जो आप उम्मीद करेंगे:

# fish
for f in (ls *.txt); echo $f; end
a b.txt
b c.txt

यह अलग तरह से काम करता है क्योंकि मछली न्यूलाइन्स पर कमांड के आउटपुट को विभाजित करती है, न कि स्पेस में।

यदि आपके पास एक ऐसा मामला है, जहां आप नई कड़ियों के बजाय रिक्त स्थान पर विभाजन करना चाहते हैं, तो fishउसके लिए बहुत ही पठनीय वाक्यविन्यास है:

for f in (ls *.txt | string split " "); echo $f; end

0

जब मैं IFS मान और रोलबैक को रीसेट करता था

# backup IFS value
O_IFS=$IFS

# reset IFS value
IFS=""

FILES=(
"2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg"
)

for file in ${FILES[@]}; do
    echo ${file}
done

# rollback IFS value
IFS=${O_IFS}

लूप से संभावित आउटपुट:

2011-09-04 21.43.02.jpg

2011-09-05 10.23.14.jpg

2011-09-09 12.31.16.jpg

2011-09-11 08.43.12.jpg

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