循环遍历 Bash 中的字符串数组?
Posted
技术标签:
【中文标题】循环遍历 Bash 中的字符串数组?【英文标题】:Loop through an array of strings in Bash? 【发布时间】:2022-01-16 20:42:53 【问题描述】:我想编写一个循环遍历 15 个字符串(可能是数组?)的脚本,这可能吗?
类似:
for databaseName in listOfNames
then
# Do something
end
【问题讨论】:
【参考方案1】:我将这种方法用于我的 GitHub 更新,我发现它很简单。
## declare an array variable
arr_variable=("kofi" "kwame" "Ama")
## now loop through the above array
for i in "$arr_variable[@]"
do
echo "$i"
done
您可以使用具有三个表达式(C 风格)的计数器来遍历 bash 数组值,以读取循环语法的所有值和索引:
declare -a kofi=("kofi" "kwame" "Ama")
# get the length of the array
length=$#kofi[@]
for (( j=0; j<$length; j++ ));
do
print (f "Current index %d with value %s\n" $j "$kofi[$j]")
done
【讨论】:
一个 bash 数组可以有孔;通过 index 循环遍历它的正确方法(也适用于关联数组)是:for i in "$!arr_variable[@]"; do printf '%d: %s\n' "$i" "$arr_variable[i]"; done
【参考方案2】:
声明一个类型为字符串的数组
declare -a StringArray=("Linux Mint" "Fedora" "Red Hat Linux" "Ubuntu" "Debian" )
使用for循环迭代字符串数组
for val in $StringArray[@]; do
echo $val
done
输出
【讨论】:
【参考方案3】:这些答案都不包括计数器...
#!/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=0; i<$arraylength; i++ ));
do
echo "index: $i, value: $array[$i]"
done
输出:
index: 0, value: one
index: 1, value: two
index: 2, value: three
【讨论】:
这应该是公认的答案,是唯一在数组元素包含空格时有效的答案。 这只是为了使用计数器的示例输出。改变它也很简单,并且工作方式相同。 最后的回声是错误的。您不需要引用常量,您需要引用扩展,或者可以安全地引用两者,如下所示:echo "$i / $arraylength : $array[$i-1]"
- 否则,如果您的 $i
包含一个 glob,它将被扩展,如果它包含一个选项卡它将被更改为空格等。
@bzeaman,当然——但如果你对这些事情很草率,那么它需要上下文分析(就像你刚才所做的那样)来证明某些事情是正确的,如果上下文发生变化或代码发生变化,则需要重新分析在不同的地方重用或出于任何其他原因可能出现意外的控制流。以稳健的方式编写它,无论上下文如何,它都是正确的。
这不适用于稀疏数组,即如果数组缺少元素。例如,如果您有数组元素 A[1]='xx'、A[4]='yy' 和 A[9]='zz',则长度将为 3,并且循环不会处理所有元素.【参考方案4】:
如何循环遍历数组,取决于是否存在换行符。用换行符分隔数组元素,数组可以称为"$array"
,否则应称为"$array[@]"
。下面的脚本会说清楚:
#!/bin/bash
mkdir temp
mkdir temp/aaa
mkdir temp/bbb
mkdir temp/ccc
array=$(ls temp)
array1=(aaa bbb ccc)
array2=$(echo -e "aaa\nbbb\nccc")
echo '$array'
echo "$array"
echo
for dirname in "$array"; do
echo "$dirname"
done
echo
for dirname in "$array[@]"; do
echo "$dirname"
done
echo
echo '$array1'
echo "$array1"
echo
for dirname in "$array1"; do
echo "$dirname"
done
echo
for dirname in "$array1[@]"; do
echo "$dirname"
done
echo
echo '$array2'
echo "$array2"
echo
for dirname in "$array2"; do
echo "$dirname"
done
echo
for dirname in "$array2[@]"; do
echo "$dirname"
done
rmdir temp/aaa
rmdir temp/bbb
rmdir temp/ccc
rmdir temp
【讨论】:
【参考方案5】:是的
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关键字(命令)创建列表,技术上称为数组:
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
CSV 变量或文件到列表中。 将内部字段分隔符从空格更改为您想要的任何内容。 在下面的示例中,它被更改为逗号
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
如果需要编号:
`
这称为反勾号。将命令放在反引号内。
`command`
在标准美式英语键盘上,它位于键盘上的数字 1 和或 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 commandline reference manual: Special meaning of certain characters or words to the shell.
【讨论】:
这是错误的。需要"$List[@]"
才能正确,带引号。 $List[@]
是错误的。 $List[*]
是错误的。试试List=( "* first item *" "* second item *" )
——你会得到for item in "$List[@]"; do echo "$item"; done
的正确行为,但不是来自任何其他变体。
是的,特殊字符会被解释,这可能是可取的,也可能不是。我更新了答案以包含。
我仍然强烈建议展示更正确/稳健的方法first。人们通常会选择看起来可行的第一个答案。如果该答案有隐藏的警告,他们可能只会在以后暴露自己。 (不只是由于缺少引用而破坏的通配符;List=( "first item" "second item" )
也将分解为 first
、item
、second
、item
)。
您还可以考虑避免使用可能导致人们解析ls
输出的示例in contravention of best practices。
你在驳斥我从未提出过的说法。我对 bash 的引用语义非常熟悉——请参阅 ***.com/tags/bash/topusers【参考方案6】:
我真正需要的是这样的:
for i in $(the_array); do something; done
例如:
for i in $(ps -aux | grep vlc | awk ' print $2 '); do kill -9 $i; done
(将杀死所有名称中带有 vlc 的进程)
【讨论】:
【参考方案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
【讨论】:
【参考方案8】:很惊讶还没有人发布这个——如果你在遍历数组时需要元素的索引,你可以这样做:
arr=(foo bar baz)
for i in $!arr[@]
do
echo $i "$arr[i]"
done
输出:
0 foo
1 bar
2 baz
我发现这比“传统”for 循环样式 (for (( i=0; i<$#arr[@]; i++ ))
) 优雅得多。
($!arr[@]
和 $i
不需要引用,因为它们只是数字;有些人会建议引用它们,但这只是个人喜好。)
【讨论】:
这确实应该是选择的答案。 1:简单易读。 2:它正确处理空白,没有 IFS 废话妨碍。 3:正确处理稀疏数组。【参考方案9】:脚本或函数的隐式数组:
除了anubhava的正确答案:如果for循环的基本语法是:
for var in "$arr[@]" ;do ...$var... ;done
bash有一个特殊案例:
运行脚本或函数时,在命令行传递的参数会被赋值给$@
数组变量,你可以通过$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'.
【讨论】:
【参考方案10】:你可以使用$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
【讨论】:
【参考方案11】:单行循环,
declare -a listOfNames=('db_a' 'db_b' 'db_c')
for databaseName in $listOfNames[@]; do echo $databaseName; done;
你会得到这样的输出,
db_a
db_b
db_c
【讨论】:
【参考方案12】:我循环遍历我的一系列项目以获取git pull
更新:
#!/bin/sh
projects="
web
ios
android
"
for project in $projects do
cd $HOME/develop/$project && git pull
end
【讨论】:
虽然这段代码 sn-p 可以解决问题,including an explanation 确实有助于提高您的帖子质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性! 你应该在你的脚本中解释IFS
变量的行为【参考方案13】:
每个 Bash 脚本/会话的可能第一行:
say() for line in "$@" ; do printf "%s\n" "$line" ; done ;
使用例如:
$ aa=( 7 -4 -e ) ; say "$aa[@]"
7
-4
-e
可以考虑:echo
将-e
解释为此处的选项
【讨论】:
【参考方案14】:试试这个。它正在运行并经过测试。
for k in "$array[@]"
do
echo $k
done
# For accessing with the echo command: echo $array[0], $array[1]
【讨论】:
这实际上不能正常工作。试试array=( "hello world" )
,或arrray=( "*" )
;在第一种情况下,它将分别打印hello
和world
,在第二种情况下,它将打印文件列表而不是*
...在 shell 中调用“tested”之前,请务必检查极端情况,其中空格和 glob 都在其中。【参考方案15】:
声明数组不适用于 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
【讨论】:
试试编辑器中的代码高亮,让你的代码看起来不错。 很高兴知道,但这个问题是关于 bash 的。 这里有很多错误。不能有带有空格的列表条目,不能有带有全局字符的列表条目。for i in $foo[*]
基本上总是错误的——for i in "$foo[@]"
是保留原始列表边界并防止全局扩展的形式。并且回声需要是echo "$i"
【参考方案16】:
本着与 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
备注
-
在第二个示例中,使用
listOfNames="RA RB R C RD"
具有相同的输出。
其他引入数据的方式包括:
标准输入(如下所列), variables, an array(已接受的答案), a file...从标准输入读取
# 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
-
可以在脚本中指定 bash IFS "field separator to line" [1] 分隔符以允许其他空格(即
IFS='\n'
,或者对于MacOS IFS='\r'
)
我也喜欢接受的答案 :) -- 我已将这些 sn-ps 作为其他有助于回答问题的方法。
脚本文件顶部包含#!/bin/bash
表示执行环境。
我花了几个月的时间才弄清楚如何简单地编写代码 :)
其他来源 (while read loop)
【讨论】:
这给人的印象是 eol 被用作字符串分隔符,因此字符串中允许使用空格。但是,带有空格的字符串被进一步分成子字符串,这是非常非常糟糕的。我认为***.com/a/23561892/1083704这个答案更好。 @Val,我添加了引用IFS
的代码注释。 (对于每个人来说,IFS
允许指定一个特定的分隔符,它允许其他空格包含在字符串中而不被分成子字符串)。
这对我不起作用。 $databaseName
只包含整个列表,因此只进行一次迭代。
@AlikElzin-kilaka 我下面的回答解决了这个问题,以便为字符串的每一行运行循环。【参考方案17】:
当然可以。
for databaseName in a b c d e f; do
# do something like: echo $databaseName
done
详情请见Bash Loops for, while and until。
【讨论】:
这种方法有什么问题?在简单的情况下,它似乎工作,然后,比@anubhava 的答案更直观。 这特别适用于命令替换,例如for year in $(seq 2000 2013)
。
问题是他问的是遍历一个数组。
如果您必须在多个位置迭代同一个数组,“declare”方法效果最好。这种方法更简洁,但灵活性较差。
为什么这不是第一名?它更干净,您只需设置一个字符串即可轻松重用数组,即DATABASES="a b c d e f"
。【参考方案18】:
你可以这样使用它:
## 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"
)
【讨论】:
请注意,"$arr[@]"
周围的双引号非常重要。 如果没有它们,for 循环将通过字符串中的任何空格分隔的子字符串来分解数组,而不是通过数组中的整个字符串元素。即:如果您有declare -a arr=("element 1" "element 2" "element 3")
,那么for i in $arr[@]
将错误地迭代6 次,因为每个字符串变成由字符串中的空格分隔的2 个子字符串,而for i in "$arr[@]"
将根据需要正确地迭代3 次,将每个字符串保持为一个单元,尽管里面有空间。
数组是否也适用于旧 bash 版本?
是的,它也适用于较旧的 bash 版本。
我认为你不需要在数组变量的名称前面写“declare -a”。它对我有用。
感谢您的评论。我花了 2 个小时思考为什么它在命令行中工作,但当我为同一组命令运行脚本时却没有。【参考方案19】:
这类似于 user2533809 的回答,但每个文件将作为单独的命令执行。
#!/bin/bash
names="RA
RB
R C
RD"
while read -r line; do
echo line: "$line"
done <<< "$names"
【讨论】:
【参考方案20】: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
【讨论】:
【参考方案21】:这也很容易阅读:
FilePath=(
"/tmp/path1/" #FilePath[0]
"/tmp/path2/" #FilePath[1]
)
#Loop
for Path in "$FilePath[@]"
do
echo "$Path"
done
【讨论】:
只有当我正确设置 IFS 变量 before FilePath 数组定义时,这个才对我很清楚并且对我有用(包括 FilePath 数组元素中的空格和变量替换):IFS=$'\n'
在这种情况下,这也可能适用于其他解决方案。【参考方案22】:
如果您使用的是 Korn shell,则有“set -A databaseName”,否则有“declare -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
它应该适用于所有外壳。
【讨论】:
不,它没有:$ bash --version
GNU bash,版本 4.3.33(0)-release (amd64-portbld-freebsd10.0) $ set -A databaseName=("db1" "db2" ....) || declare -a databaseName=("db1" "db2" ....)
bash: 意外标记附近的语法错误` ('以上是关于循环遍历 Bash 中的字符串数组?的主要内容,如果未能解决你的问题,请参考以下文章