बैश - एक सरणी रिवर्स


16

क्या किसी सरणी को उलटने का एक सरल तरीका है?

#!/bin/bash

array=(1 2 3 4 5 6 7)

echo "${array[@]}"

इसलिए मुझे मिलेगा: के 7 6 5 4 3 2 1
बजाय:1 2 3 4 5 6 7

जवाबों:


15

मैंने प्रश्न का लिखित रूप में उत्तर दिया है, और यह कोड सरणी को उलट देता है। (सरणी को उल्टा किए बिना तत्वों को रिवर्स ऑर्डर में प्रिंट करना forअंतिम तत्व से शून्य तक गिना जाने वाला एक लूप है।) यह एक मानक "पहले और अंतिम" स्वैप है।

array=(1 2 3 4 5 6 7)

min=0
max=$(( ${#array[@]} -1 ))

while [[ min -lt max ]]
do
    # Swap current first and last elements
    x="${array[$min]}"
    array[$min]="${array[$max]}"
    array[$max]="$x"

    # Move closer
    (( min++, max-- ))
done

echo "${array[@]}"

यह विषम और लंबाई की सरणियों के लिए काम करता है।


कृपया ध्यान दें कि यह विरल सरणियों के लिए काम नहीं करता है।
इसहाक

@ यदि आप को संभालने की आवश्यकता है तो StackOverflow पर एक समाधान है।
रोमीमा


18

एक और अपारंपरिक दृष्टिकोण:

#!/bin/bash

array=(1 2 3 4 5 6 7)

f() { array=("${BASH_ARGV[@]}"); }

shopt -s extdebug
f "${array[@]}"
shopt -u extdebug

echo "${array[@]}"

आउटपुट:

६ ५ ४ ३ २ १ १

यदि extdebugसक्षम किया गया है, तो सरणी BASH_ARGVमें एक फ़ंक्शन होता है , जो रिवर्स ऑर्डर में सभी स्थितीय पैरामीटर होता है।


यह एक भयानक चाल है!
वैलेन्टिन Bajrami

15

अपरंपरागत दृष्टिकोण (सभी शुद्ध नहीं bash):

  • यदि किसी सरणी में सभी तत्व केवल एक वर्ण हैं (जैसे प्रश्न में) तो आप उपयोग कर सकते हैं rev:

    echo "${array[@]}" | rev
  • अन्यथा:

    printf '%s\n' "${array[@]}" | tac | tr '\n' ' '; echo
  • और यदि आप उपयोग कर सकते हैं zsh:

    echo ${(Oa)array}

बस देख रहा था tac, catयाद करने के लिए काफी अच्छा के विपरीत , धन्यवाद!
नाथ

3
हालांकि मुझे इसका विचार पसंद है rev, मुझे यह उल्लेख करने की आवश्यकता है कि revदो अंकों के साथ संख्याओं के लिए सही ढंग से काम नहीं करेगा। उदाहरण के लिए, 12 रेव का उपयोग करने का एक सरणी तत्व के रूप में मुद्रित किया जाएगा 21। इसे आजमाइए ;-)
जॉर्ज वासिलियौ

@GeorgeVasiliou हाँ, यह तभी काम करेगा जब सभी तत्व एक वर्ण (संख्या, अक्षर, विराम चिह्न, ...) हों। इसलिए मैंने दूसरा, अधिक सामान्य समाधान भी दिया।
जिमीज

8

यदि आप वास्तव में किसी अन्य सरणी में रिवर्स चाहते हैं:

reverse() {
    # first argument is the array to reverse
    # second is the output array
    declare -n arr="$1" rev="$2"
    for i in "${arr[@]}"
    do
        rev=("$i" "${rev[@]}")
    done
}

फिर:

array=(1 2 3 4)
reverse array foo
echo "${foo[@]}"

देता है:

4 3 2 1

यह उन मामलों को सही ढंग से संभालना चाहिए जहां एक सरणी सूचकांक गायब है, कहते हैं कि आपके पास था array=([1]=1 [2]=2 [4]=4), जिसमें 0 से उच्चतम सूचकांक तक लूपिंग अतिरिक्त, खाली, तत्वों को जोड़ सकता है।


इस एक के लिए धन्यवाद, यह बहुत अच्छी तरह से काम करता है, हालांकि किसी कारण के लिए shellcheckदो चेतावनियां छापती हैं: array=(1 2 3 4) <-- SC2034: array appears unused. Verify it or export it.और इसके लिए:echo "${foo[@]}" <-- SC2154: foo is referenced but not assigned.
nath

1
@ नाथ वे अप्रत्यक्ष रूप से उपयोग किए जाते हैं, यही वह declareरेखा है जिसके लिए है।
मुरु

चतुर, लेकिन ध्यान दें कि declare -n4.3 से पहले बैश संस्करणों में काम नहीं करता है।
जी-मैन ने

8

जगह में सरणी पदों की अदला-बदली करने के लिए (यहां तक ​​कि विरल सरणियों के साथ) (bash 3.0 के बाद से):

#!/bin/bash
# Declare an sparse array to test:
array=([5]=101 [6]=202 [10]=303 [11]=404 [20]=505 [21]=606 [40]=707)
echo "Initial array values"
declare -p array

swaparray(){ local temp; temp="${array[$1]}"
             array[$1]="${array[$2]}"
             array[$2]="$temp"
           }

ind=("${!array[@]}")                         # non-sparse array of indexes.

min=-1; max="${#ind[@]}"                     # limits to one before real limits.
while [[ min++ -lt max-- ]]                  # move closer on each loop.
do
    swaparray "${ind[min]}" "${ind[max]}"    # Exchange first and last
done

echo "Final Array swapped in place"
declare -p array
echo "Final Array values"
echo "${array[@]}"

निष्पादन पर:

./script
Initial array values
declare -a array=([5]="101" [6]="202" [10]="303" [11]="404" [20]="505" [21]="606" [40]="707")

Final Array swapped in place
declare -a array=([5]="707" [6]="606" [10]="505" [11]="404" [20]="303" [21]="202" [40]="101")

Final Array values
707 606 505 404 303 202 101

पुराने बैश के लिए, आपको लूप (बश में (2.04 से)) का उपयोग $aकरने और अनुगामी स्थान से बचने के लिए उपयोग करने की आवश्यकता है:

#!/bin/bash

array=(101 202 303 404 505 606 707)
last=${#array[@]}

a=""
for (( i=last-1 ; i>=0 ; i-- ));do
    printf '%s%s' "$a" "${array[i]}"
    a=" "
done
echo

2.03 के बाद से बैश के लिए:

#!/bin/bash
array=(101 202 303 404 505 606 707)
last=${#array[@]}

a="";i=0
while [[ last -ge $((i+=1)) ]]; do 
    printf '%s%s' "$a" "${array[ last-i ]}"
    a=" "
done
echo

इसके अलावा (बिटवाइस निगेटिव ऑपरेटर का उपयोग करके) (4.2+ के बाद से):

#!/bin/bash
array=(101 202 303 404 505 606 707)
last=${#array[@]}

a=""
for (( i=0 ; i<last ; i++ )); do 
    printf '%s%s' "$a" "${array[~i]}"
    a=" "
done
echo

ऋणात्मक सदस्यता के साथ पीछे से एक सरणी के तत्वों को संबोधित करते हुए लगता है कि 4.3 से पहले बैश संस्करणों में काम नहीं करना चाहिए।
जी-मैन ने

1
दरअसल, ऋणात्मक संख्याओं को संबोधित करते हुए 4.2-अल्फा में बदल दिया गया था। और नकारात्मक मान वाली स्क्रिप्ट उस संस्करण से काम करती है। @ जी-मैन पी। अनुक्रमित सरणियों के लिए नकारात्मक सदस्यता, अब अधिकतम निर्दिष्ट सूचकांक + से ऑफसेट के रूप में व्यवहार किया जाता है, लेकिन बैश-हैकर्स गलत तरीके से रिपोर्ट करते हैं। 4.1 संख्यात्मक रूप से अनुक्रमित सरणियों को नकारात्मक अनुक्रमित का उपयोग करके अंत तक पहुँचा जा सकता है
इसहाक

3

बदसूरत, अचूक, लेकिन एक-लाइनर:

eval eval echo "'\"\${array['{$((${#array[@]}-1))..0}']}\"'"

सरल नहीं है, लेकिन कम eval eval echo "'\"\${array[-'{1..${#array[@]}}']}\"'":।
इसहाक

और विरल सरणियों के लिए भी:ind=("${!array[@]}");eval eval echo "'\"\${array[ind[-'{1..${#array[@]}}']]}\"'"
इसहाक

@ इस्साक लेकिन अब एक-लाइनर और केवल बदसूरत सरणी संस्करण के लिए बदसूरत और अस्वीकार्य नहीं है। (हालांकि, छोटे सरणियों के लिए पाइप की तुलना में अभी भी तेज होना चाहिए।)
user23013

खैर, तकनीकी रूप से, यह "वन-लाइनर" है; एक आदेश नहीं, हाँ, लेकिन यह "एक लाइनर" है। मैं सहमत हूं, हां, बहुत बदसूरत और रखरखाव की समस्या है, लेकिन खेलने के लिए मजेदार है।
इसहाक

1

हालाँकि मैं कुछ नया नहीं बताने जा रहा हूँ और मैं tacसरणी को उलटने के लिए भी इस्तेमाल करूँगा , हालाँकि मैं bash संस्करण 4.4 का उपयोग करके bellow सिंगल लाइन सॉल्यूशन का उल्लेख करने के लिए चिंतित होऊंगा:

$ read -d'\n' -a array < <(printf '%s\n' "${array[@]}" |tac)

परिक्षण:

$ array=(1 2 3 4 5 6 10 11 12)
$ echo "${array[@]}"
1 2 3 4 5 6 10 11 12
$ read -d'\n' -a array < <(printf '%s\n' "${array[@]}"|tac)
$ echo "${array[@]}"
12 11 10 6 5 4 3 2 1

ध्यान रखें कि रीड नाम के अंदर का नाम मूल सरणी के रूप में नाम है, इसलिए अस्थायी संग्रहण के लिए कोई सहायक सरणी आवश्यक नहीं है।

IFS को समायोजित करके वैकल्पिक कार्यान्वयन:

$ IFS=$'\n' read -d '' -a array < <(printf '%s\n' "${array[@]}"|tac);declare -p array
declare -a array=([0]="12" [1]="11" [2]="10" [3]="6" [4]="5" [5]="4" [6]="3" [7]="2" [8]="1")

पुनश्च: मुझे लगता है कि अलग-अलग बैश बिलिन फ़ंक्शन कार्यान्वयन के कारण उपरोक्त समाधान bashबलो संस्करण में काम नहीं करेगा ।4.4read


IFSसंस्करण काम करता है लेकिन यह भी प्रिंट कर रहा है: declare -a array=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="10" [7]="11" [8]="12")। बैश का उपयोग करना 4.4-5। आपको ;declare -p arrayपहली पंक्ति के अंत में हटा दिया गया था, फिर यह काम करता है ...
नाथ

1
@ बाथ declare -pअसली सरणी (इंडेक्स और कंटेंट) को प्रिंट करने का एक त्वरित तरीका है। आपको declare -pअपनी वास्तविक स्क्रिप्ट में इस आदेश की आवश्यकता नहीं है । यदि आपके सरणियों के असाइनमेंट में कुछ गलत हो जाता है तो आप एक मामले में समाप्त हो सकते हैं ${array[0]}="1 2 3 4 5 6 10 11 12"= एक ही सूचकांक में संग्रहीत सभी मान - गूंज का उपयोग करके आपको कोई अंतर नहीं दिखाई देगा। एक त्वरित सरणी प्रिंटआउट का उपयोग करने के लिए declare -p arrayआप वास्तविक सरणी इंडोल और प्रत्येक इंडेक्स में संबंधित मान वापस करेंगे।
जॉर्ज वासिलिउ

@ नाथ वैसे, read -d'\n'विधि आपके काम नहीं आई?
जॉर्ज वासिलियौ

read -d'\n'ठीक काम करता है।
नाथ

आह्ह्ह आपको मिल गया! सोर्री :-)
नाथ

1

एक मनमाना सरणी को उलटने के लिए (जिसमें किसी भी मान के साथ कितने भी तत्व हो सकते हैं):

के साथ zsh:

array_reversed=("${(@Oa)array}")

bash4.4+ के साथ , यह देखते हुए कि bashचर में एनयूएल बाइट्स नहीं हो सकते हैं, आप tac -s ''एनयूएल सीमांकित के रूप में मुद्रित तत्वों पर GNU का उपयोग कर सकते हैं :

readarray -td '' array_reversed < <(
  ((${#array[@]})) && printf '%s\0' "${array[@]}" | tac -s '')

POSIXly, POSIX खोल सरणी उल्टा करने के लिए ( $@, से बना $1, $2...):

code='set --'
n=$#
while [ "$n" -gt 0 ]; do
  code="$code \"\${$n}\""
  n=$((n - 1))
done
eval "$code"

1

शुद्ध बैश समाधान, वन-लाइनर के रूप में काम करेगा।

$: for (( i=${#array[@]}-1; i>=0; i-- ))
>  do rev[${#rev[@]}]=${array[i]}
>  done
$: echo  "${rev[@]}"
7 6 5 4 3 2 1

अच्छा है!!! धन्यवाद; यहाँ एक लाइनर कॉपी करने के लिए :-) `सरणी = (1 2 3 4 5 6 7); के लिए ((i = $ {# सरणी [@]} - 1; i> = 0; i--)); पर क्लिक करें [$ {# रेव [@]}] = $ {सरणी [i]}; किया हुआ; इको "$ {रेव [@]}"
नाथ

करना rev+=( "${array[i]}" )सरल लगता है।
इसहाक

एक का छह, दूसरे का आधा दर्जन। मैं उस वाक्य रचना के लिए नहीं हूँ, लेकिन इसका कोई कारण नहीं है - सिर्फ पूर्वाग्रह और पसंद। आप करो आप।
पॉल हॉल्जेस

-1

आप का उपयोग करने पर भी विचार कर सकते हैं seq

array=(1 2 3 4 5 6 7)

for i in $(seq $((${#array[@]} - 1)) -1 0); do
    echo ${array[$i]}
done

फ्रीबेस में आप -1 वेतन वृद्धि पैरामीटर को छोड़ सकते हैं:

for i in $(seq $((${#array[@]} - 1)) 0); do
    echo ${array[$i]}
done

ध्यान दें कि यह सरणी को उल्टा नहीं करता है, यह केवल रिवर्स ऑर्डर में प्रिंट करता है।
रोज़ा

सहमत, मेरी बात भी एक विकल्प के रूप सूचकांकों का उपयोग पर विचार करना था ..
एम Modugno

-2

दे घुमा के

array=(1 2 3 4 5 6 7)
echo "${array[@]} " | tac -s ' '

या

array=(1 2 3 4 5 6 7)
reverse=$(echo "${array[@]} " | tac -s ' ')
echo ${reverse[@]}

परिणाम

६ ५ ४ ३ २ १ १

संस्करण

$ tac --version
tac (GNU coreutils) 8.28

1
tacपहले ही उल्लेख किया गया था: unix.stackexchange.com/a/412874/260978 , unix.stackexchange.com/a/467924/260978 , unix.stackexchange.com/a/413176/260978
Olorin
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.