¿Recorrer una serie de cadenas en Bash?

1503

Quiero escribir un script que recorra 15 cadenas (¿matriz posiblemente?) ¿Es eso posible?

Algo como:

for databaseName in listOfNames
then
  # Do something
end
Mes.
fuente

Respuestas:

2392

Puedes usarlo así:

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

También funciona para la declaración de matriz multilínea

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

Eso es posible, por supuesto.

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

Ver Bash Loops para, mientras y hasta para más detalles.

4ndrew
fuente
18
¿Cuál es el problema con este enfoque? En casos simples, parece funcionar y, por lo tanto, es más intuitivo que la respuesta de @anubhava.
Dr. Jan-Philip Gehrcke
17
Esto funciona particularmente bien con la sustitución de comandos, por ejemplo for year in $(seq 2000 2013).
Brad Koch
23
El problema es que preguntó sobre iterar a través de una matriz.
mgalgs
20
El enfoque 'declarar' funciona mejor si tiene que iterar sobre la misma matriz en más de un lugar. Este enfoque es más limpio pero menos flexible.
StampyCode
15
¿Por qué no es este # 1? Es más limpio, y se puede reutilizar fácilmente la matriz simplemente mediante el establecimiento de una cadena, es decir, DATABASES="a b c d e f".
Nerdmaster
201

Ninguna de esas respuestas incluye un 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

Salida:

1  /  3  :  one
2  /  3  :  two
3  /  3  :  three
caktux
fuente
77
Esta debería ser la respuesta aceptada, es la única que funciona cuando los elementos de la matriz contienen espacios.
jesjimher
1
Eso es solo por el resultado del ejemplo con un contador. También es bastante trivial cambiar eso, y funciona igual.
caktux
77
El eco al final es defectuoso. No necesita citar constantes, necesita citar expansiones, o puede citar con seguridad las dos cosas de la siguiente manera: de lo echo "$i / ${arraylength} : ${array[$i-1]}"contrario, si $icontiene un globo, se expandirá, si contiene una pestaña, se cambiará a un espacio, etc.
Charles Duffy
10
@bzeaman, claro, pero si eres descuidado con esas cosas, entonces requiere un análisis contextual (como acabas de hacer) para probar algo correcto, y un nuevo análisis si ese contexto cambia o el código se reutiliza en un lugar diferente o para cualquier otra razón es posible un flujo de control inesperado. Escríbalo de manera robusta y es correcto independientemente del contexto.
Charles Duffy
55
Esto no funcionará para una matriz dispersa, es decir, si a la matriz le faltan elementos. Por ejemplo, si tiene elementos de matriz A [1] = 'xx', A [4] = 'yy' y A [9] = 'zz', la longitud será 3 y el bucle no procesará todos los elementos .
Sr. Ed
145

si

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

Salida:

Item1
Item2
Item3
Item4

Para preservar espacios; entradas de lista de comillas simples o dobles y expansiones de listas de comillas dobles.

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

Salida:

Item 1
Item 2
Item 3
Item 4

Para hacer una lista en varias líneas

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

Salida:

Item1
Item2
Item3
Item4


Lista simple variable

List=( Item1 Item2 Item3 )

o

List=(
      Item1 
      Item2 
      Item3
     )

Mostrar la lista de variables:

echo ${List[*]}

Salida:

Item1 Item2 Item3

Recorra la lista:

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

Salida:

Item1
Item2
Item3

Cree una función para recorrer una lista:

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

Usando la palabra clave declare (comando) para crear la lista, que técnicamente se denomina matriz:

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

Salida:

element 1
element 2
element 3

Crear una matriz asociativa. Un diccionario:

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

Salida:

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

Variables CVS o archivos en una lista .
Cambiar el separador de campo interno de un espacio, a lo que quieras.
En el siguiente ejemplo, se cambia a una coma

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

Salida:

Item 1
Item 2
Item 3

Si necesita numerarlos:

` 

Esto se llama un tic de retroceso. Ponga el comando dentro de los ticks posteriores.

`commend` 

Está al lado del número uno en su teclado o encima de la tecla de tabulación. En un teclado estándar de inglés americano.

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

Salida es:

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

Familiarizarse con el comportamiento de bashes:

Crea una lista en un archivo

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

Lea el archivo de lista en una lista y visualice

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 referencia de línea de comandos de BASH: significado especial de ciertos caracteres o palabras para el shell.

Fuego en el cielo
fuente
77
ESTO ESTÁ MAL. Debe "${List[@]}"ser correcto, con las comillas . ${List[@]}Está Mal. ${List[*]}Está Mal. Intente List=( "* first item *" "* second item *" ): obtendrá el comportamiento correcto para for item in "${List[@]}"; do echo "$item"; done, pero no de cualquier otra variante.
Charles Duffy
Sí, los caracteres especiales se interpretan, lo que puede o no ser deseable. Actualicé la respuesta para incluir.
FireInTheSky
2
Todavía sugeriría encarecidamente mostrar primero el enfoque más correcto / robusto . La gente suele tomar la primera respuesta que parece funcionar; Si esa respuesta tiene advertencias ocultas, solo pueden exponerse más tarde. (No es sólo comodines que se rompen por la falta de citar, List=( "first item" "second item" )se divide en first, item, second, itemtambién).
Charles Duffy
También puede considerar evitar el uso de un ejemplo que podría llevar a las personas a analizar la lsproducción, en contravención de las mejores prácticas .
Charles Duffy
1
Estás refutando afirmaciones que nunca hice. Estoy bastante familiarizado con la semántica de citas de bash - ver stackoverflow.com/tags/bash/topusers
Charles Duffy
108

En el mismo espíritu que la respuesta 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. Sin espacios en blanco en los nombres:

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. En el segundo ejemplo, usar listOfNames="RA RB R C RD"tiene la misma salida.

Otras formas de incorporar datos incluyen:

Leer 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. el golpe del IFS "separador de campo a la línea" [ 1 ] delimitador puede ser especificado en el guión para permitir que otros espacios en blanco (es decir,IFS='\n' , o para MacOS IFS='\r')
  2. También me gusta la respuesta aceptada :) - He incluido estos fragmentos como otras formas útiles que también responden la pregunta.
  3. Incluso #!/bin/bash en la parte superior del archivo de script indica el entorno de ejecución.
  4. Me tomó meses descubrir cómo codificar esto simplemente :)

Otras fuentes ( mientras lee el bucle )

usuario2533809
fuente
Esto crea la impresión de que eol se utiliza como separadores de cadenas y, por lo tanto, se permiten espacios en blanco dentro de las cadenas. Sin embargo, las cadenas con espacios en blanco se separan aún más en subcadenas, lo cual es muy, muy malo. Creo que esta respuesta stackoverflow.com/a/23561892/1083704 es mejor.
Val
1
@Val, agregué un comentario de código con una referencia a IFS. (Para todos, IFSpermite especificar un delimitador específico, lo que permite incluir otros espacios en blanco en las cadenas sin separarlos en subcadenas).
user2533809
77
Esto no funciona para mi. $databaseNamesolo contiene la lista completa, por lo tanto, solo realiza una única iteración.
AlikElzin-kilaka
@ AlikElzin-kilaka Mi respuesta a continuación resuelve este problema para que el ciclo se ejecute para cada línea de la cadena.
Jamie
42

Puedes usar la sintaxis 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
fuente
31

Sorprende que nadie haya publicado esto todavía; si necesita los índices de los elementos mientras recorre la matriz, puede hacer esto:

arr=(foo bar baz)

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

Salida:

0 foo
1 bar
2 baz

Me parece mucho más elegante que el estilo "tradicional" de bucle (for (( i=0; i<${#arr[@]}; i++ )) ).

( ${!arr[@]}y $ino necesita ser citado porque son solo números; algunos sugerirían citarlos de todos modos, pero eso es solo una preferencia personal).

tckmn
fuente
2
Esta realmente debería ser la respuesta elegida. 1: es simple y fácil de leer. 2: Maneja los espacios en blanco correctamente, no hay tonterías IFS que se interpongan en el camino. 3: Maneja matrices dispersas correctamente.
mrm
20

Esto también es fácil de leer:

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

#Loop
for Path in "${FilePath[@]}"
do
    echo "$Path"
done
Treethawat Thanawachiramate
fuente
44
Este es claro y funcionó para mí (incluso con espacios y sustitución de variables en los elementos de la matriz FilePath) solo cuando configuré la variable IFS correctamente antes de la definición de la matriz FilePath: IFS=$'\n' esto también puede funcionar para otras soluciones en este escenario.
Alan Forsyth
8

Matriz implícita para script o funciones:

Además de la respuesta correcta de anubhava : Si la sintaxis básica para loop es:

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

hay un caso especial en:

Cuando se ejecuta un script o una función, argumentos adoptados en las líneas de comandos se pueden asignar a $@variable de matriz, se puede acceder por $1, $2, $3, y así sucesivamente.

Esto se puede completar (para prueba) por

set -- arg1 arg2 arg3 ...

Un bucle sobre esta matriz podría escribirse simplemente:

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

¡Tenga en cuenta que el trabajo reservado inno está presente y tampoco tiene un nombre de matriz!

Muestra:

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

Tenga en cuenta que esto es lo mismo que

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

Luego en un guión :

#!/bin/bash

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

Guarda esto en un script myscript.sh, chmod +x myscript.shy luego

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

Lo mismo en una función :

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

Entonces

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

o solo

for databaseName in db_one db_two db_three
do
  echo $databaseName
done
nroose
fuente
7

Manera simple :

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
fuente
6

La matriz declare no funciona para el shell Korn. Use el siguiente ejemplo para el 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
fuente
2
pruebe el resaltador de código en el editor para que su código se vea bien.
Paloma
55
Es bueno saberlo, pero esta pregunta es sobre bash.
Brad Koch
1
Bichos de Lotsa aquí. No se pueden tener entradas de lista con espacios, no se pueden tener entradas de lista con caracteres globales. for i in ${foo[*]}básicamente es siempre lo incorrecto: for i in "${foo[@]}"es la forma que conserva los límites de la lista original y evita la expansión global. Y el eco debe serecho "$i"
Charles Duffy, el
4

Esto es similar a la respuesta del usuario 2533809, pero cada archivo se ejecutará como un comando separado.

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

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

Si está utilizando el shell Korn, hay " set -A databaseName ", de lo contrario hay " declare -a databaseName "

Para escribir un guión que funcione en todos los shells,

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

done

Debería funcionar en todos los proyectiles.

El águila inteligente
fuente
3
No, no lo hace: $ bash --versionGNU bash, versión 4.3.33 (0) -release (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)bash: error de sintaxis cerca de token inesperado `('
Bernie Reiter
2

Prueba esto. Está funcionando y probado.

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

# For accessing with the echo command: echo ${array[0]}, ${array[1]}
Simpal Kumar
fuente
3
Esto en realidad no funciona correctamente. Intenta array=( "hello world" ), o arrray=( "*" ); en el primer caso se imprimirá helloy por worldseparado, en el segundo imprimirá una lista de archivos en lugar de la*
Charles Duffy
66
... antes de llamar a algo "probado" en el shell, asegúrese de verificar los casos de las esquinas, entre los que se encuentran los espacios en blanco y los globos.
Charles Duffy
2

Posible primera línea de cada script / sesión de Bash:

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

Utilice, por ejemplo:

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

Puede considerar: echointerpreta -ecomo opción aquí

elf12
fuente
2

Bucle de línea única,

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

obtendrás una salida como esta,

db_a
db_b
db_c
Mohideen bin Mohammed
fuente
2

Lo que realmente necesitaba para esto era algo como esto:

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

Por ejemplo:

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

(Mataría todos los procesos con vlc en su nombre)

Mahdi-Malv
fuente
1

Recorro una variedad de mis proyectos para una git pullactualización:

#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
    cd  $HOME/develop/$project && git pull
end
jiahut
fuente
77
Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código. Por favor, también trate de no saturar su código con comentarios explicativos, ¡esto reduce la legibilidad tanto del código como de las explicaciones!
kayess