ChatGPT解决这个技术问题 Extra ChatGPT

循环遍历 Bash 中的字符串数组?

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

就像是:

for databaseName in listOfNames
then
  # Do something
end

N
Nam G VU

你可以像这样使用它:

## 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 版本?
我认为您不需要在数组变量的名称前写“declare -a”。这个对我有用。
感谢您的评论。我花了 2 个小时思考为什么它在命令行中起作用,但在我为同一组命令运行脚本时却没有。
@AndrewKoster 在这里你去:gnu.org/software/bash/manual/html_node/… 省略他们示例中显示的偏移量相当于 ${array[@]:0} (0 偏移量 AKA 从开头开始)。您可以轻松测试此行为:echo "${array[@]}",并且应该清楚为什么它与 for 一起工作。在任何时候使用变量时,双引号都是很好的做法,以确保预期的行为;它们在这里与数组一起使用的事实是巧合。
O
Ohad Schneider

当然,这是可能的。

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)
问题是他询问了遍历数组的问题。
如果您必须在多个地方迭代同一个数组,则“声明”方法效果最好。这种方法更简洁但不太灵活。
为什么这不是#1?它更简洁,只需设置一个字符串,即DATABASES="a b c d e f",您就可以轻松地重用数组。
c
catchdave

这些答案中没有一个包括计数器......

#!/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,它将被扩展,如果它包含一个选项卡它将被更改为空格等。
@CharlesDuffy $i 不会包含 glob,因为它是本示例中的循环计数器,因此您可以控制它的值。
@bzeaman,当然——但是如果你对这些事情很草率,那么它需要上下文分析(就像你刚才所做的那样)来证明某些事情是正确的,如果上下文发生变化或者代码在不同的地方被重用,或者为了无论出于何种其他原因,意外的控制流都是可能的。以稳健的方式编写它,无论上下文如何,它都是正确的。
这不适用于稀疏数组,即如果数组缺少元素。例如,如果您有数组元素 A[1]='xx'、A[4]='yy' 和 A[9]='zz',则长度将为 3,循环不会处理所有元素.
C
Community

是的

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" ) 也将分解为 firstitemseconditem)。
您还可以考虑避免使用可能导致人们解析 ls 输出 in contravention of best practices 的示例。
你在驳斥我从未提出过的说法。我对 bash 的引用语义非常熟悉 - 请参阅 stackoverflow.com/tags/bash/topusers
C
Community

本着与 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 RC RD" 具有相同的输出。

其他引入数据的方法包括:

标准输入(如下所列),

变量,

一个数组(接受的答案),

一份文件...

从标准输入读取

# 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“字段分隔符到行”[1] 分隔符以允许其他空格(即 IFS='\n',或者对于 MacOS IFS='\r')我也喜欢接受的答案:) -- 我已经将这些片段作为其他有用的方式来回答这个问题。在脚本文件顶部包含#!/bin/bash 表示执行环境。我花了几个月的时间才弄清楚如何简单地编写代码:)

其他来源 (while read loop)


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

您可以使用 ${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

t
tckmn

很惊讶还没有人发布这个——如果你在遍历数组时需要元素的索引,你可以这样做:

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:它正确处理稀疏数组。
T
Treethawat Thanawachiramate

这也很容易阅读:

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

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

仅当我正确设置 IFS 变量之前 FilePath 数组定义:IFS=$'\n' 这可能适用于其他在这种情况下也有解决方案。
r
rashedcs

简单的方法:

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}

K
Kofi

我将这种方法用于我的 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
n
nroose
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

F
F. Hauri - Give Up GitHub

脚本或函数的隐式数组:

除了 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'.

P
Peter Mortensen

声明数组不适用于 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"
P
Peter Mortensen

尝试这个。它正在工作并经过测试。

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

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

这实际上不能正常工作。试试 array=( "hello world" )arrray=( "*" );在第一种情况下,它将分别打印 helloworld,在第二种情况下,它将打印文件列表而不是 *
...在 shell 中调用“测试”的东西之前,一定要检查极端情况,其中空白和 glob 都在其中。
J
Jamie

这类似于 user2533809 的答案,但每个文件都将作为单独的命令执行。

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

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

T
The smart Eagle

如果您使用 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:意外标记 `(' 附近的语法错误
M
Mahdi-Malv

我真正需要的是这样的:

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

例如:

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

(会杀死所有名称中带有 vlc 的进程)


D
Dan Bray

如何循环遍历数组取决于换行符的存在。用换行符分隔数组元素,数组可以称为 "$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

S
Saurabh Dhage

声明一个类型为字符串的数组

declare -a StringArray=("Linux Mint" "Fedora" "Red Hat Linux" "Ubuntu" "Debian" )

使用 for 循环迭代字符串数组

for val in ${StringArray[@]}; do
   echo $val
done

输出

https://i.stack.imgur.com/AV59o.png


P
Peter Mortensen

每个 Bash 脚本/会话的可能第一行:

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

使用例如:

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

可以考虑:echo-e 解释为此处的选项


M
Mohideen bin Mohammed

单行循环,

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

你会得到这样的输出,

db_a
db_b
db_c

P
Peter Mortensen

我循环遍历我的项目数组以进行 git pull 更新:

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

虽然此代码段可能会解决问题,但 including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性注释来挤满你的代码,这会降低代码和解释的可读性!
您应该在脚本中解释 IFS 变量行为