题 在Bash中循环遍历一串字符串?


我想编写一个循环遍历15个字符串的脚本(可能是数组?)这可能吗?

就像是:

for databaseName in listOfNames
then
  # Do something
end

963
2018-01-16 13:21


起源




答案:


你可以像这样使用它:

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

1546
2018-01-16 13:24



不知何故,$ i周围的“”需要访问每个数组元素,你能解释一下为什么或指向一些我能阅读的材料来理解它吗?非常感谢! - olala
在BASH中,使用变量引用变量更安全 "" 对于的情况 $i 可能包含空格或shell可扩展字符。 - anubhava
如果引用如上所示,它确实可以使用元素中的空格。你最好自己测试一下。 - anubhava
有什么好处 declare -a?我不能做更简单的事情,比如 arr=("element1" "element2" "element3")? - Ignorant
declare 要么 typeset 是一种在shell脚本中声明变量的明确方法。 - anubhava


当然,这是可能的。

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

看到 Bash循环,while和until 详情。


523
2018-01-16 13:24



这种方法有什么问题?在简单的情况下,它似乎工作,然后,比@ anubhava的答案更直观。 - Jan-Philip Gehrcke
这对于命令替换特别有效,例如 for year in $(seq 2000 2013)。 - Brad Koch
问题是他询问迭代数组。 - mgalgs
如果你必须在多个地方迭代同一个数组,'declare'方法效果最好。这种方法更简洁但灵活性更低。 - StampyCode
为什么不是这个#1?它更干净,只需设置一个字符串即可轻松重用数组,即 DATABASES="a b c d e f"。 - Nerdmaster


这些答案都不包括反击......

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

133
2018-03-16 03:29



这应该是接受的答案,是数组元素包含空格时唯一有效的答案。 - jesjimher
当然,你应该从0开始我的理智。 - GManNickG
这只是为了示例的计数器输出。改变它也很微不足道,并且工作方式也一样。 - caktux
最后的回声是错误的。您不需要引用常量,您需要引用扩展,或者可以安全地引用两者,如下所示: echo "$i / ${arraylength} : ${array[$i-1]}"  - 否则,如果你的 $i 包含一个它将被展开的glob,如果它包含一个标签,它将被更改为空格等。 - Charles Duffy
@bzeaman,当然 - 但是如果你对这些东西很草率,那么它需要进行上下文分析(正如你刚才所做的那样)来证明一些正确的东西,并重新分析该上下文的变化或代码是否在不同的地方重用或者用于无论出于何种其他原因,都可能出现意外的控用稳健的方式写它,无论上下文如何都是正确的。 - Charles Duffy


本着与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. 我花了几个月的时间才弄清楚如何编写这个简单:)

其他来源 (而读循环


93
2018-01-17 05:22



这会产生eol用作字符串分隔符的印象,因此字符串中允许使用空格。但是,具有空格的字符串进一步分为子串,这非常非常糟糕。我认为这个答案 stackoverflow.com/a/23561892/1083704 更好。 - Val
@Val,我添加了代码注释和引用 IFS。 (为了所有人, IFS 让我们指定一个特定的分隔符,它允许其他空格包含在字符串中而不分成子字符串)。 - user2533809
这对我不起作用。 $databaseName 只包含整个列表,因此只进行一次迭代。 - AlikElzin-kilaka
@ AlikElzin-kilaka我在下面的回答解决了这个问题,以便为字符串的每一行运行循环。 - Jamie


您可以使用的语法 ${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
2018-02-17 06:13



引用你的扩展: echo "$i"  - 否则,一个条目 "/etc/*" 将被扩展为一个文件列表 /etc 在回声期间。 - Charles Duffy
@CharlesDuffy谢谢。完成 - Fizer Khan
简单,清晰,类似于我用python的方式。 - Evhz


 

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

输出:

Item1
Item2
Item3
Item4

多行

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[*]}

保留空间;单引号或双引号列表条目和双引号列表扩展: 

List=(' Item 1 '
      ' Item 2' 
      ' Item 3'
     )
for item in "${List[@]}"; 
  do 
    echo "$item"
  done 

输出:

 Item 1
 Item 2
 Item 3

使用declare关键字(命令)创建列表,技术上称为数组: 

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` 

它位于键盘上的第一个旁边,或者位于Tab键上方。在标准的美国英语键盘上。

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

更熟悉bashes行为:

在文件中创建列表

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命令行参考手册:shell中某些字符或单词的特殊含义。


20
2017-09-12 08:58



这是错的。 需要是 "${List[@]}" 为了正确, 用引号。 ${List[@]} 是错的。 ${List[*]} 是错的。尝试 List=( "* first item *" "* second item *" )  - 你会得到正确的行为 for item in "${List[@]}"; do echo "$item"; done,但不是来自任何其他变体。 - Charles Duffy
是的特殊字符会被解释,这可能是也可能不是。我更新了包含的答案。 - FireInTheSky
我仍然强烈建议展示更正确/更健全的方法 第一。人们通常会采取第一个看起来会起作用的答案;如果答案有隐藏的警告,他们可能只会在以后暴露自己。 (这不仅仅是由于缺乏引用而破坏的通配符; List=( "first item" "second item" ) 将被打破 first, item, second, item 以及)。 - Charles Duffy
您也可以考虑避免使用可能导致人们解析的示例 ls 输出, 违反最佳做法。 - Charles Duffy
你反驳我从未做过的主张。我对bash的引用语义非常熟悉 - 请参阅 stackoverflow.com/tags/bash/topusers - Charles Duffy


这也很容易阅读:

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

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

17
2017-07-03 04:03



只有当我正确设置IFS变量时,这个对我来说很清楚(包括在FilePath数组元素中使用空格和变量替换) 之前 FilePath数组定义: IFS=$'\n'  在这种情况下,这也适用于其他解决方案。 - Alan Forsyth


声明数组不适用于Korn shell。对Korn shell使用以下示例:

promote_sla_chk_lst="cdi xlob"

set -A promote_arry $promote_sla_chk_lst

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

4
2017-11-02 19:20



在编辑器中尝试代码荧光笔,使代码看起来很好。 - dove
很高兴知道,但这个问题是关于bash。 - Brad Koch
谁使用Korn?好的乐队,但那就是它...... - Mike Purcell
Lotsa臭虫在这里。不能有空格的列表条目,不能有带有glob字符的列表条目。 for i in ${foo[*]} 基本上总是错的 - for i in "${foo[@]}" 是保留原始列表边界并防止glob扩展的表单。回声需要 echo "$i" - Charles Duffy


脚本或函数的隐式数组:

此外 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.shchmod +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'.

4
2017-12-05 08:01





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

3
2018-03-19 20:26





如果你使用的是Korn shell,那就有“set -A databaseName “,否则有”声明-a databaseName

编写一个处理所有shell的脚本,

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

done

它应该适用于所有炮弹。


2
2017-09-25 08:32



不,它没有: $ bash --version GNU bash,版本4.3.33(0)-release(amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....) bash:意外令牌附近的语法错误`('' - Bernie Reiter