Loop através de uma matriz de seqüências de caracteres no Bash?

1503

Eu quero escrever um script que faça um loop através de 15 strings (possivelmente array?) Isso é possível?

Algo como:

for databaseName in listOfNames
then
  # Do something
end
Mo.
fonte

Respostas:

2392

Você pode usá-lo assim:

## 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

Também funciona para declaração de matriz com várias linhas

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

Isso é possível, é claro.

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

Consulte Bash Loops para, enquanto e até para obter detalhes.

4ndrew
fonte
18
Qual é o problema com essa abordagem? Em casos simples, parece funcionar e, portanto, é mais intuitivo do que a resposta de @ anubhava.
Dr. Jan-Philip Gehrcke
17
Isso funciona particularmente bem com a substituição de comandos, por exemplo for year in $(seq 2000 2013).
Brad Koch
23
O problema é que ele perguntou sobre a iteração através de uma matriz.
mgalgs
20
A abordagem 'declarar' funciona melhor se você precisar repetir a mesma matriz em mais de um local. Essa abordagem é mais limpa, mas menos flexível.
StampyCode
15
Por que esse não é o número 1? É mais limpo, e você pode facilmente reutilizar a matriz apenas configurando uma string, ou seja DATABASES="a b c d e f",.
Nerdmaster 02/07
201

Nenhuma dessas respostas inclui um contador ...

#!/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

Resultado:

1  /  3  :  one
2  /  3  :  two
3  /  3  :  three
caktux
fonte
7
Essa deve ser a resposta aceita, é a única que funciona quando os elementos da matriz contêm espaços.
jesjimher
1
Isso é apenas pelo bem da saída do exemplo com um contador. Também é bastante trivial mudar isso e funciona da mesma maneira.
caktux
7
O eco no final é de buggy. Você não precisa citar constantes, é necessário citar expansões ou pode apenas citar as duas coisas com segurança da seguinte maneira: echo "$i / ${arraylength} : ${array[$i-1]}"- caso contrário, se você $icontiver um globo, ele será expandido, se contiver uma guia, será alterado para um espaço, etc.
Charles Duffy
10
@bzeaman, claro - mas se você é desleixado com essas coisas, isso requer análise contextual (como você acabou de fazer) para provar algo correto e re-análise se esse contexto mudar ou se o código for reutilizado em um local diferente ou para qualquer que seja o motivo, é possível um fluxo de controle inesperado. Escreva da maneira robusta e correta, independentemente do contexto.
Charles Duffy
5
Isso não funcionará para uma matriz esparsa, ou seja, se houver elementos ausentes na matriz. Por exemplo, se você tiver elementos de matriz A [1] = 'xx', A [4] = 'aa' e A [9] = 'zz', o comprimento será 3 e o loop não processará todos os elementos .
Ed Ed
145

sim

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

Resultado:

Item1
Item2
Item3
Item4

Preservar espaços; entradas da lista de cotações simples ou duplas e expansões da lista de cotações duplas.

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

Resultado:

Item 1
Item 2
Item 3
Item 4

Para fazer a lista em várias linhas

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

Resultado:

Item1
Item2
Item3
Item4


Variável de lista simples

List=( Item1 Item2 Item3 )

ou

List=(
      Item1 
      Item2 
      Item3
     )

Exiba a variável da lista:

echo ${List[*]}

Resultado:

Item1 Item2 Item3

Percorra a lista:

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

Resultado:

Item1
Item2
Item3

Crie uma função para percorrer uma lista:

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

Usando a palavra-chave declare (comando) para criar a lista, que é tecnicamente chamada de matriz:

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

Resultado:

element 1
element 2
element 3

Criando uma matriz associativa. Um dicionário:

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

Resultado:

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

Variáveis ​​ou arquivos CVS em uma lista .
Alterando o separador interno de campos de um espaço para o que você quiser.
No exemplo abaixo, ele é alterado para vírgula

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

Resultado:

Item 1
Item 2
Item 3

Se for necessário numerá-los:

` 

isso é chamado de retorno. Coloque o comando dentro de back ticks.

`commend` 

É o próximo ao número um do teclado e ou acima da tecla Tab. Em um teclado de inglês americano padrão.

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

A saída é:

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

Familiarizando-se com o comportamento do bash:

Crie uma lista em um arquivo

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

Leia o arquivo da lista em uma lista e exiba

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

Manual de referência da linha de comando do BASH: significado especial de certos caracteres ou palavras para o shell.

Fogo no céu
fonte
7
ISTO ESTÁ ERRADO. Precisa "${List[@]}"estar correto, com as aspas . ${List[@]}está errado. ${List[*]}está errado. Experimente List=( "* first item *" "* second item *" )- você obterá o comportamento correto para for item in "${List[@]}"; do echo "$item"; done, mas não de qualquer outra variante.
Charles Duffy
Sim, caracteres especiais são interpretados, o que pode ou não ser desejável. Eu atualizei a resposta para incluir.
FireInTheSky
2
Eu ainda sugeriria fortemente mostrar primeiro a abordagem mais correta / robusta . As pessoas costumam ter a primeira resposta que parece funcionar; se essa resposta tiver advertências ocultas, elas poderão se expor mais tarde. (Não é apenas curingas que são quebrados por falta de citação; List=( "first item" "second item" )será dividido em first, item, second, itemtambém).
Charles Duffy
Você também pode evitar o uso de um exemplo que possa levar as pessoas a analisar a lsprodução, contrariando as melhores práticas .
Charles Duffy
1
Você está refutando reivindicações que eu nunca fiz. Eu estou bem familiarizado com a semântica de citação do bash - veja stackoverflow.com/tags/bash/topusers #
9662 Charles Duffy
108

No mesmo espírito da resposta de 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. Nenhum espaço em branco nos nomes:

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

Notas

  1. No segundo exemplo, usar listOfNames="RA RB R C RD"tem a mesma saída.

Outras maneiras de trazer dados incluem:

Leia de 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. o delimitador bash IFS "separador de campo para linha" [ 1 ] pode ser especificado no script para permitir outros espaços em branco (por exemplo IFS='\n', ou para MacOS IFS='\r')
  2. Também gosto da resposta aceita :) - Incluí esses trechos como outras maneiras úteis que também respondem à pergunta.
  3. A inclusão #!/bin/bashna parte superior do arquivo de script indica o ambiente de execução.
  4. Levei meses para descobrir como codificar isso simplesmente :)

Outras fontes ( enquanto lê o loop )

user2533809
fonte
Isso cria a impressão de que eol é usado como separador de cadeias e, portanto, espaços em branco são permitidos nas cadeias. No entanto, seqüências de caracteres com espaços em branco são ainda mais separadas em substrings, o que é muito, muito ruim. Penso que esta resposta stackoverflow.com/a/23561892/1083704 é melhor.
Val
1
@ Val, eu adicionei comentário de código com uma referência a IFS. (Para todos, IFSdeixe-se especificar um delimitador específico, que permita que outro espaço em branco seja incluído nas seqüências de caracteres sem ser separado em substrings).
user2533809
7
Isso não funciona para mim. $databaseNamecontém apenas a lista inteira, portanto, apenas uma iteração única.
AlikElzin-kilaka
@ AlikElzin-kilaka Minha resposta abaixo resolve esse problema para que o loop seja executado em todas as linhas da string.
Jamie
42

Você pode usar a sintaxe de ${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
Fizer Khan
fonte
31

Surpreso que ninguém tenha postado isso ainda - se você precisar dos índices dos elementos enquanto estiver percorrendo a matriz, poderá fazer o seguinte:

arr=(foo bar baz)

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

Resultado:

0 foo
1 bar
2 baz

Acho isso muito mais elegante que o estilo "tradicional" de loop for ( for (( i=0; i<${#arr[@]}; i++ ))).

( ${!arr[@]}e $inão precisam ser citados porque são apenas números; alguns sugerem citá-los de qualquer maneira, mas isso é apenas preferência pessoal.)

tckmn
fonte
2
Esta realmente deve ser a resposta escolhida. 1: É simples e fácil de ler. 2: Ele lida com o espaço em branco corretamente, nenhuma bobagem do IFS está atrapalhando. 3: Ele lida com matrizes esparsas corretamente.
Mrm 04/04/19
20

Também é fácil de ler:

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

#Loop
for Path in "${FilePath[@]}"
do
    echo "$Path"
done
Treethawat Thanawachiramate
fonte
4
Este é claro e funcionou para mim (inclusive com espaços e substituição de variável nos elementos da matriz FilePath) somente quando eu defino a variável IFS corretamente antes da definição da matriz FilePath: IFS=$'\n' Isso pode funcionar para outras soluções também neste cenário.
Alan Forsyth
8

Matriz implícita para script ou funções:

Além da resposta correta do anubhava : Se a sintaxe básica do loop for:

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

existe um caso especial em:

Ao executar um script ou uma função, os argumentos passados em linhas de comando será atribuído a $@variável de matriz, você pode acessar por $1, $2, $3e assim por diante.

Isso pode ser preenchido (para teste) por

set -- arg1 arg2 arg3 ...

Um loop sobre esse array poderia ser escrito simplesmente:

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

Observe que o trabalho reservado innão está presente e também não há nome para o array!

Amostra:

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: ....

Observe que isso é igual a

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

Depois, em um script :

#!/bin/bash

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

Salve isso em um script myscript.she chmod +x myscript.sh, em seguida,

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

O mesmo em uma função :

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

Então

myfunc item1 tiem2 time3
Working about 'item1'.
Working about 'tiem2'.
Working about 'time3'.
F. Hauri
fonte
7
listOfNames="db_one db_two db_three"
for databaseName in $listOfNames
do
  echo $databaseName
done

ou apenas

for databaseName in db_one db_two db_three
do
  echo $databaseName
done
Nroose
fonte
7

Maneira simples :

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}
rashedcs
fonte
6

A matriz declare não funciona para o shell Korn. Use o exemplo abaixo para o shell Korn:

promote_sla_chk_lst="cdi xlob"

set -A promote_arry $promote_sla_chk_lst

for i in ${promote_arry[*]};
    do
            echo $i
    done
Bhanu
fonte
2
tente o marcador de código no editor para tornar seu código com boa aparência.
pomba
5
É bom saber, mas esta pergunta é sobre bash.
Brad Koch
1
Muitos erros aqui. Não pode ter entradas de lista com espaços, não pode ter entradas de lista com caracteres glob. for i in ${foo[*]}é basicamente sempre a coisa errada - for i in "${foo[@]}"é a forma que preserva os limites da lista original e evita a expansão global. E o eco precisa serecho "$i"
Charles Duffy
4

Isso é semelhante à resposta do usuário2533809, mas cada arquivo será executado como um comando separado.

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

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

Se você estiver usando o shell Korn, haverá " set -A databaseName ", caso contrário, haverá " declare -a databaseName "

Para escrever um script trabalhando em todas as conchas,

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

done

Deve funcionar em todas as conchas.

A águia inteligente
fonte
3
Não, não é: $ bash --versionGNU bash, versão 4.3.33 (0) -release (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)bash: erro de sintaxe próximo ao token inesperado `('
Bernie Reiter
2

Tente isso. Está funcionando e testado.

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

# For accessing with the echo command: echo ${array[0]}, ${array[1]}
Simpal Kumar
fonte
3
Na verdade, isso não funciona corretamente. Tente array=( "hello world" ), ou arrray=( "*" ); no primeiro caso, ele irá imprimir helloe worldseparadamente, na segunda ele irá imprimir uma lista de arquivos em vez do*
Charles Duffy
6
... antes de chamar algo "testado" no shell, verifique as caixas de canto, onde estão os espaços em branco e os globs.
Charles Duffy
2

Possível primeira linha de cada script / sessão do Bash:

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

Use, por exemplo:

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

Pode considerar: echointerpreta -ecomo opção aqui

elf12
fonte
2

Loop de linha única,

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

você obterá uma saída como esta,

db_a
db_b
db_c
Mohideen bin Mohammed
fonte
2

O que eu realmente precisava para isso era algo assim:

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

Por exemplo:

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

(Mataria todos os processos com vlc em seu nome)

Mahdi-Malv
fonte
1

Eu percorrer uma matriz dos meus projetos para uma git pullatualização:

#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
    cd  $HOME/develop/$project && git pull
end
jiahut
fonte
7
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código. Por favor, tente também não sobrecarregar seu código com comentários explicativos, pois isso reduz a legibilidade do código e das explicações!
Kayess