Проведіть масив рядків у Bash?


1499

Я хочу написати сценарій, який проходить через 15 рядків (можливо, масив?) Це можливо?

Щось на зразок:

for databaseName in listOfNames
then
  # Do something
end

Відповіді:


2385

Ви можете використовувати його так:

## declare an array variable
declare -a arr=("element1" "element2" "element3")

## now loop through the above array
for i in "${arr[@]}"
do
   echo "$i"
   # or do whatever with individual element of the array
done

# You can access them using echo "${arr[0]}", "${arr[1]}" also

Також працює для багаторядкового оголошення масиву

declare -a arr=("element1" 
                "element2" "element3"
                "element4"
                )

775

Це можливо, звичайно.

for databaseName in a b c d e f; do
  # do something like: echo $databaseName
done 

Див Bash Loops для, в той час як і до для деталей.


18
У чому проблема такого підходу? У простих випадках, здається, це працює і, отже, більш інтуїтивно зрозуміло, ніж відповідь @ anubhava.
Доктор Ян-Філіп Геркк

17
Це особливо добре працює при заміні команд, наприклад for year in $(seq 2000 2013).
Бред Кох

22
Проблема полягає в тому, що він запитав про ітерацію через масив.
mgalgs

20
Підхід «оголосити» найкраще працює, якщо вам доведеться повторити один і той же масив у більш ніж одному місці. Цей підхід є більш чистим, але менш гнучким.
StampyCode

15
Чому це не №1? Це чистіше, і ви можете легко використовувати масив просто встановивши рядок, тобто DATABASES="a b c d e f".
Nerdmaster

201

Жодна з цих відповідей не включає лічильник ...

#!/bin/bash
## declare an array variable
declare -a array=("one" "two" "three")

# get length of an array
arraylength=${#array[@]}

# use for loop to read all values and indexes
for (( i=1; i<${arraylength}+1; i++ ));
do
  echo $i " / " ${arraylength} " : " ${array[$i-1]}
done

Вихід:

1  /  3  :  one
2  /  3  :  two
3  /  3  :  three

7
Це повинна бути прийнята відповідь, є єдиною, яка працює, коли елементи масиву містять пробіли.
jesjimher

1
Це просто заради виведення прикладу з лічильником. Це також досить тривіально змінити, і працює так само.
caktux

7
Відлуння в кінці баггі. Вам не потрібно цитувати константи, вам потрібно цитувати розширення, або можете сміливо просто цитувати наступне: echo "$i / ${arraylength} : ${array[$i-1]}"- інакше, якщо ваш $iмістить глобус, він буде розширений, якщо він містить вкладку, його буде змінено на простір тощо
Чарльз Даффі

10
@bzeaman, звичайно - але якщо ви неохайні щодо таких речей, то для його підтвердження потрібен контекстуальний аналіз (як ви це зробили), і повторний аналіз, якщо цей контекст змінюється або код використовується повторно в іншому місці або для з будь-якої іншої причини можливий несподіваний потік управління. Напишіть це надійним способом, і це правильно незалежно від контексту.
Чарльз Даффі

5
Це не буде працювати для розрідженого масиву, тобто якщо в масиві відсутні елементи. Наприклад, якщо у вас є елементи масиву A [1] = 'xx', A [4] = 'yy' і A [9] = 'zz', довжина буде 3, і цикл не обробить усі елементи .
Містер Ед

144

Так

for Item in Item1 Item2 Item3 Item4 ;
  do
    echo $Item
  done

Вихід:

Item1
Item2
Item3
Item4

Для збереження просторів; записи одиничного або подвійного списку цитат та подвійні розширення списку цитат.

for Item in 'Item 1' 'Item 2' 'Item 3' 'Item 4' ;
  do
    echo "$Item"
  done

Вихід:

Item 1
Item 2
Item 3
Item 4

Щоб скласти список у кількох рядках

for Item in Item1 \
            Item2 \
            Item3 \
            Item4
  do
    echo $Item
  done

Вихід:

Item1
Item2
Item3
Item4


Проста змінна список

List=( Item1 Item2 Item3 )

або

List=(
      Item1 
      Item2 
      Item3
     )

Показати змінну списку:

echo ${List[*]}

Вихід:

Item1 Item2 Item3

Прокрутіть список:

for Item in ${List[*]} 
  do
    echo $Item 
  done

Вихід:

Item1
Item2
Item3

Створіть функцію для перегляду списку:

Loop(){
  for item in ${*} ; 
    do 
      echo ${item} 
    done
}
Loop ${List[*]}

Використовуючи ключове слово (команду) оголошення для створення списку, який технічно називається масивом:

declare -a List=(
                 "element 1" 
                 "element 2" 
                 "element 3"
                )
for entry in "${List[@]}"
   do
     echo "$entry"
   done

Вихід:

element 1
element 2
element 3

Створення асоціативного масиву. Словник:

declare -A continent

continent[Vietnam]=Asia
continent[France]=Europe
continent[Argentina]=America

for item in "${!continent[@]}"; 
  do
    printf "$item is in ${continent[$item]} \n"
  done

Вихід:

 Argentina is in America
 Vietnam is in Asia
 France is in Europe

Змінні CVS або файли у списку .
Зміна внутрішнього роздільника поля від простору до того, що вам завгодно.
У наведеному нижче прикладі він змінюється комою

List="Item 1,Item 2,Item 3"
Backup_of_internal_field_separator=$IFS
IFS=,
for item in $List; 
  do
    echo $item
  done
IFS=$Backup_of_internal_field_separator

Вихід:

Item 1
Item 2
Item 3

Якщо потрібно пронумерувати їх:

` 

це називається задній кліщ. Поставте команду всередину задніх кліщів.

`commend` 

Він знаходиться поруч із номером один на клавіатурі та над клавішею вкладки. На стандартній американській англійській мовній клавіатурі.

List=()
Start_count=0
Step_count=0.1
Stop_count=1
for Item in `seq $Start_count $Step_count $Stop_count`
    do 
       List+=(Item_$Item)
    done
for Item in ${List[*]}
    do 
        echo $Item
    done

Вихід:

Item_0.0
Item_0.1
Item_0.2
Item_0.3
Item_0.4
Item_0.5
Item_0.6
Item_0.7
Item_0.8
Item_0.9
Item_1.0

Познайомившись з поведінкою на базі:

Створіть список у файлі

cat <<EOF> List_entries.txt
Item1
Item 2 
'Item 3'
"Item 4"
Item 7 : *
"Item 6 : * "
"Item 6 : *"
Item 8 : $PWD
'Item 8 : $PWD'
"Item 9 : $PWD"
EOF

Прочитайте файл зі списком до списку та покажіть

List=$(cat List_entries.txt)
echo $List
echo '$List'
echo "$List"
echo ${List[*]}
echo '${List[*]}'
echo "${List[*]}"
echo ${List[@]}
echo '${List[@]}'
echo "${List[@]}"

Довідковий посібник з командної лінії BASH: Спеціальне значення певних символів або слів до оболонки.


7
ЦЕ НЕПРАВИЛЬНО. Потрібно "${List[@]}"бути правильним, з цитатами . ${List[@]}неправильно. ${List[*]}неправильно. Спробуйте List=( "* first item *" "* second item *" )- ви отримаєте правильну поведінку for item in "${List[@]}"; do echo "$item"; done, але не для будь-якого іншого варіанту.
Чарльз Даффі

Так, інтерпретуються спеціальні характеристики, що може бути, а може і не бути бажаним. Я оновив відповідь, щоб включити.
FireInTheSky

2
Я все-таки настійно пропоную показати спочатку більш правильний / надійний підхід . Люди часто приймають першу відповідь, схоже, що це спрацює; якщо ця відповідь має приховані застереження, вони можуть викрити себе лише пізніше. (Це не тільки спеціальні символи, які розбиті на відсутність квотування, List=( "first item" "second item" )буде розбитий first, item, second, itemа).
Чарльз Даффі

Ви також можете розглянути можливість уникнення використання прикладу, який може привести людей до розбору lsрезультатів, всупереч кращим практикам .
Чарльз Даффі

1
Ви спростовуєте вимоги, яких я ніколи не висловлював. Я цілком знайомий із семантикою цитування Баша - див. Stackoverflow.com/tags/bash/topusers
Чарльз Даффі

108

У тому ж дусі, що і відповідь 4ndrew:

listOfNames="RA
RB
R C
RD"

# To allow for other whitespace in the string:
# 1. add double quotes around the list variable, or
# 2. see the IFS note (under 'Side Notes')

for databaseName in "$listOfNames"   #  <-- Note: Added "" quotes.
do
  echo "$databaseName"  # (i.e. do action / processing of $databaseName here...)
done

# Outputs
# RA
# RB
# R C
# RD

B. У назвах немає пробілів:

listOfNames="RA
RB
R C
RD"

for databaseName in $listOfNames  # Note: No quotes
do
  echo "$databaseName"  # (i.e. do action / processing of $databaseName here...)
done

# Outputs
# RA
# RB
# R
# C
# RD

Примітки

  1. У другому прикладі використання listOfNames="RA RB R C RD"має той самий вихід.

Інші способи залучення даних включають:

Читайте з stdin

# line delimited (each databaseName is stored on a line)
while read databaseName
do
  echo "$databaseName"  # i.e. do action / processing of $databaseName here...
done # <<< or_another_input_method_here
  1. bash IFS "роздільник поля на рядок" [ 1 ] роздільник може бути визначений у сценарії, щоб дозволити інший пробіл (тобто IFS='\n', або для MacOS IFS='\r')
  2. Мені подобається також прийнята відповідь :) - Ці фрагменти я включаю як інші корисні способи, які також відповідають на питання.
  3. Включення #!/bin/bashу верхній частині файлу сценарію вказує на середовище виконання.
  4. Мені знадобилося кілька місяців, щоб зрозуміти, як це просто кодувати :)

Інші джерела ( під час читання циклу )


Це створює враження, що eol використовується як роздільник рядків, а отже, білі простори дозволені всередині рядків. Однак рядки з пробілами далі розділяються на підрядки, що дуже погано. Я думаю, що ця відповідь stackoverflow.com/a/23561892/1083704 є кращою.
Валь

1
@Val, я додав коментар до коду із посиланням на IFS. (Для кожного, IFSдозвольте вказати конкретний роздільник, який дозволяє включати інші рядки пробілів до рядків, не розділяючи їх на підрядки).
користувач2533809

7
Це не працює для мене. $databaseNameпросто містить весь список, таким чином, робить лише одну ітерацію.
AlikElzin-kilaka

@ AlikElzin-kilaka Моя відповідь нижче вирішує цю проблему, щоб цикл виконувався для кожного рядка рядка.
Джеймі

42

Ви можете використовувати синтаксис ${arrayName[@]}

#!/bin/bash
# declare an array called files, that contains 3 values
files=( "/etc/passwd" "/etc/group" "/etc/hosts" )
for i in "${files[@]}"
do
    echo "$i"
done

31

Здивований, що цього ще ніхто не опублікував - якщо вам потрібні індекси елементів під час циклічного перегляду масиву, ви можете зробити це:

arr=(foo bar baz)

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

Вихід:

0 foo
1 bar
2 baz

Я вважаю це набагато більш елегантним, ніж "традиційний" стиль для циклу ( for (( i=0; i<${#arr[@]}; i++ ))).

( ${!arr[@]}і $iне потрібно їх цитувати, тому що це просто цифри; деякі пропонують цитувати їх все одно, але це лише особисті переваги.)


2
Це справді має бути обраною відповіддю. 1: Це легко і легко для читання. 2: Це правильно обробляє пробіли, жодна дурниця IFS не стає на шляху. 3: Правильно обробляє рідкісні масиви.
MRM

20

Це також легко читати:

FilePath=(
    "/tmp/path1/"    #FilePath[0]
    "/tmp/path2/"    #FilePath[1]
)

#Loop
for Path in "${FilePath[@]}"
do
    echo "$Path"
done

4
Це зрозуміло і працює для мене (в тому числі з пробілами та змінною підстановкою в елементах масиву FilePath) лише тоді, коли я правильно встановив змінну IFS перед визначенням масиву FilePath: IFS=$'\n' Це може працювати і для інших рішень у цьому сценарії.
Алан Форсайт

8

Неявний масив для сценарію або функцій:

Окрім правильної відповіді anubhava : Якщо базовий синтаксис циклу:

for var in "${arr[@]}" ;do ...$var... ;done

є окремий випадок в:

При виконанні сценарію або функції, аргументи , що передаються в командному рядку буде присвоєно $@змінної масиву, ви можете отримати доступ з допомогою $1, $2, $3і так далі.

Це можна заповнити (для тесту)

set -- arg1 arg2 arg3 ...

Петля над цим масивом можна було б написати просто:

for item ;do
    echo "This is item: $item."
  done

Зауважте, що зарезервованої роботи inнемає, і назви масиву теж немає!

Зразок:

set -- arg1 arg2 arg3 ...
for item ;do
    echo "This is item: $item."
  done
This is item: arg1.
This is item: arg2.
This is item: arg3.
This is item: ....

Зауважте, що це те саме, що і

for item in "$@";do
    echo "This is item: $item."
  done

Потім у сценарій :

#!/bin/bash

for item ;do
    printf "Doing something with '%s'.\n" "$item"
  done

Зберегти в скрипті myscript.sh, chmod +x myscript.sh, то

./myscript.sh arg1 arg2 arg3 ...
Doing something with 'arg1'.
Doing something with 'arg2'.
Doing something with 'arg3'.
Doing something with '...'.

Те саме у функції :

myfunc() { for item;do cat <<<"Working about '$item'."; done ; }

Тоді

myfunc item1 tiem2 time3
Working about 'item1'.
Working about 'tiem2'.
Working about 'time3'.

7
listOfNames="db_one db_two db_three"
for databaseName in $listOfNames
do
  echo $databaseName
done

або просто

for databaseName in db_one db_two db_three
do
  echo $databaseName
done

7

Простий спосіб:

arr=("sharlock"  "bomkesh"  "feluda" )  ##declare array

len=${#arr[*]}  # it returns the array length

#iterate with while loop
i=0
while [ $i -lt $len ]
do
    echo ${arr[$i]}
    i=$((i+1))
done


#iterate with for loop
for i in $arr
do
  echo $i
done

#iterate with splice
 echo ${arr[@]:0:3}

6

Масив оголошення не працює для оболонки Korn. Використовуйте нижченаведений приклад для оболонки Korn:

promote_sla_chk_lst="cdi xlob"

set -A promote_arry $promote_sla_chk_lst

for i in ${promote_arry[*]};
    do
            echo $i
    done

2
спробуйте виділити код у редакторі, щоб ваш код виглядав добре.
голуб

5
Добре знати, але це питання стосується баш.
Бред Кох

1
Тут багато клопів. Не може бути записів у списку з пробілами, не може бути записів у списках із глобусними символами. for i in ${foo[*]}в основному завжди неправильна річ - for i in "${foo[@]}"це форма, яка зберігає межі оригінального списку і запобігає розширенню глобального списку. І відлуння має бутиecho "$i"
Чарльз Даффі

4

Це схоже на відповідь користувача2533809, але кожен файл буде виконуватися як окрема команда.

#!/bin/bash
names="RA
RB
R C
RD"

while read -r line; do
    echo line: "$line"
done <<< "$names"

3

Якщо ви використовуєте оболонку Korn, є " встановити -A databaseName ", інакше є " оголосити -a databaseName "

Щоб написати сценарій, що працює над усіма оболонками,

 set -A databaseName=("db1" "db2" ....) ||
        declare -a databaseName=("db1" "db2" ....)
# now loop 
for dbname in "${arr[@]}"
do
   echo "$dbname"  # or whatever

done

Це має бути робота на всіх снарядах.


3
Ні, це не так: $ bash --versionGNU bash, версія 4.3.33 (0) -випуск (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)bash: помилка синтаксису біля несподіваного маркера `('
Берні Рейтер

2

Спробуйте це. Це працює і тестується.

for k in "${array[@]}"
do
    echo $k
done

# For accessing with the echo command: echo ${array[0]}, ${array[1]}

3
Це насправді не працює правильно. Спробуйте array=( "hello world" ), або arrray=( "*" ); у першому випадку він буде надруковано helloта worldокремо, у другому надрукує список файлів замість*
Чарльз Даффі

6
... перш ніж називати щось "перевірене" в оболонці, обов'язково перевірте кутові корпуси, серед яких пробіли та глобуси.
Чарльз Даффі

2

Можливий перший рядок кожного сценарію / сеансу Bash:

say() { for line in "${@}" ; do printf "%s\n" "${line}" ; done ; }

Використовуйте, наприклад:

$ aa=( 7 -4 -e ) ; say "${aa[@]}"
7
-4
-e

Може розглянути: echoінтерпретує -eяк варіант тут


2

Однорядковий циклічний цикл,

 declare -a listOfNames=('db_a' 'db_b' 'db_c')
 for databaseName in ${listOfNames[@]}; do echo $databaseName; done;

ви отримаєте такий вихід,

db_a
db_b
db_c

2

Що мені справді було потрібно для цього, було щось подібне:

for i in $(the_array); do something; done

Наприклад:

for i in $(ps -aux | grep vlc  | awk '{ print $2 }'); do kill -9 $i; done

(Убиває всі процеси з vlc на їх ім'я)


1

Я переглядаю масив своїх проектів для git pullоновлення:

#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
    cd  $HOME/develop/$project && git pull
end

7
Хоча цей фрагмент коду може вирішити питання, зокрема пояснення дійсно допомагає покращити якість вашої публікації. Пам’ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду. Будь ласка, намагайтеся не переповнювати свій код пояснювальними коментарями, це зменшує читабельність і коду, і пояснень!
kayess
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.