【问题标题】:Duplicate output looping through multiple values in while loop bash在while循环bash中重复输出循环多个值
【发布时间】:2019-02-17 08:26:20
【问题描述】:

我正在尝试使用 bash 中的 while 循环遍历 3 个文件的内容。它们包含名称、aws 帐户和 aws 帐号的列表。

但循环不正确,它不断重复列表中的名字和列表中的第一个 aws 环境。

这是我看到的输出:

AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-lab

这些是我要读取的文件:

aws_users_all="source_files/aws_users_all.txt"

示例输出:

aadatiya
abigailcharles 
tdunphy
broberts

下一个文件:

aws_env_list="source_files/aws_environments_all.txt"

示例输出:

company-lab
company-stage
company-nonprod
company-prod

最后一个文件: aws_account_numbers="source_files/aws_account_numbers.txt" 示例输出:

  123456789191
  987654321211
  456721231213
  123213512321

这是循环错误的代码:

ofile=source_files/aws_access_keys/company-aws-access-keys-all-accounts.csv
while IFS= read -r aws_user_name
  do 
    while IFS= read -r aws_key
    do
      while IFS= read -r aws_account_num
      do
        user_lives_here=$(aws iam get-user --user-name "$aws_user_name" --profile="$aws_key" 2> /dev/null  | jq -r '.User.UserName')
        if [[ -z "$user_lives_here" ]]; then
          printf "AWS user name: %s does not exist in AWS account: %s\\n(%s)" "$aws_user_name" "$aws_key" "$aws_account_num"
        else
          echo "$aws_user_name,$user_access_key1,$key1_date_created,$key1_last_used,$key1AgeDays,$aws_key,$aws_account_num" >> $ofile
      fi
      done < "$aws_account_numbers"
    done < "$aws_env_list"
  done < "$aws_users_all"

如果我取出一个级别(帐号级别),脚本会按预期运行并产生以下输出:

AWS user name: aadatiya does not exist in AWS account: company-lab
AWS user name: aadatiya does not exist in AWS account: company-bill
AWS user name: aadatiya does not exist in AWS account: company-stage
AWS user name: aadatiya does not exist in AWS account: company-dlab
AWS user name: aadatiya does not exist in AWS account: company-nonprod
AWS user name: aadatiya does not exist in AWS account: company-prod
AWS user name: aadatiya does not exist in AWS account: company-govcloud-admin-nonprod
AWS user name: abigailcharles does not exist in AWS account: company-lab
AWS user name: abigailcharles does not exist in AWS account: company-bill
AWS user name: abigailcharles does not exist in AWS account: company-stage
AWS user name: abigailcharles does not exist in AWS account: company-dlab
AWS user name: abigailcharles does not exist in AWS account: company-nonprod
AWS user name: abigailcharles does not exist in AWS account: company-prod
AWS user name: abigailcharles does not exist in AWS account: company-govcloud-admin-nonprod

我刚刚注释掉了这个级别,它可以工作:

  #while IFS= read -r aws_account_num
  #do
  #done

如何正确执行此操作,以便循环遍历每个名​​称、aws 帐户和 aws 帐号,以便每个条目显示一次?

【问题讨论】:

  • 您能否添加预期的示例输出以及所有“aws-*”文件的示例内容
  • 好吧,您的脚本运行正常。对于每个用户名,您检查每个键。在其中,foreach 键,您检查每个帐号。我是否正确假设您要检查的是每个用户名只使用相应的密钥和帐号一次?
  • 谢谢大家。我已经用我想要的示例输出更新了 OP,并且更清楚地解释了我正在尝试做的事情。
  • 你能添加示例输入吗?
  • 这些文件是否相关? aws_user_name、aws_key 和 aws_account_num 是否相关?在这种情况下,您可以使用单个循环,而不是您现在拥有的嵌套循环。请参阅这篇文章以了解如何一次读取三个文件:Looping through the content of a file in Bash

标签: bash


【解决方案1】:

一种方法是在处理之前将所有输入数据加载到数组中:

IFS= mapfile -t aws_users <"$aws_users_all"
IFS= mapfile -t aws_envs <"$aws_env_list"
IFS= mapfile -t aws_acct_nums <"$aws_account_numbers"

for aws_user_name in "${aws_users[@]}" ; do
    for idx in ${!aws_envs[*]} ; do
        aws_key=${aws_envs[idx]}
        aws_account_num=${aws_acct_nums[idx]}

        # Code using $aws_user_name, $aws_key, and $aws_account_num
        ...
    done
done

请注意,mapfile 是在 Bash 4.0 中引入的,因此如果您使用的是旧版本的 Bash,这将不起作用。请参阅 Read lines from a file into a Bash array 了解适用于旧版本的替代方案(但请注意,接受的答案非常糟糕)。

将文件加载到内存中似乎是个坏主意,但在实践中不太可能成为问题。现代机器有很多内存,而 Bash 在几乎所有事情上都非常慢。如果要加载到内存中的数据过多,则可能有方式过多的数据无法使用 Bash 代码逐行处理。

【讨论】:

  • 小心,使用 mapfile,您必须将整个 3 个文档加载到内存中!
  • 我添加了一段关于将数据加载到内存中的内容。
【解决方案2】:

有一种使用paste命令的方法:

while read aws_user_name aws_key aws_account_num ;do
    printf "User: %-16s Key: %-22s AccNum: %s\n" \
        $aws_user_name $aws_key $aws_account_num
    # ...
done < <(
    paste "$aws_users_all" "$aws_env_list" "$aws_account_numbers"
)

示例:

User: aadatiya         Key: company-lab            AccNum: 123456789191
User: abigailcharles   Key: company-stage          AccNum: 987654321211
User: tdunphy          Key: company-nonprod        AccNum: 456721231213
User: broberts         Key: company-prod           AccNum: 123213512321

注意:当每个文件确实包含 1 个单词时,这将很简单。

...如果您打算在源文件中使用空间,您可以使用另一个分隔符

while IFS=@ read -r aws_user_name aws_key aws_account_num ;do
    printf "User: %-16s Key: %-22s AccNum: %s\n" \
        "$aws_user_name" "$aws_key" "$aws_account_num"
    # ....
done < <(
    paste -d@ "$aws_users_all" "$aws_env_list" "$aws_account_numbers"
)

或者在下玩IO

@chepner附近解决方案:

exec 3<$aws_env_list
exec 4<$aws_account_numbers
while read aws_user_name ;do
    read -u 3 aws_key
    read -u 4 aws_account_num
    printf "User: %-16s Key: %-22s AccNum: %s\n" \
        "$aws_user_name" "$aws_key" "$aws_account_num"
    # ...
done <$aws_users_all
exec 3<&-
exec 4<&-

注意:最后两行将关闭 FD 3 和 4。

【讨论】:

    【解决方案3】:

    您希望 一个 循环调用 read 三次 次,使用三个不同的文件描述符。

    while IFS= read -r aws_user_name
          IFS= read -r aws_key <&3
          IFS= read -r aws_account_num <&4; do
        ...
    done < "$aws_users_all" 3< "$aws_env_list" 4< "$aws_account_numbers"
    

    【讨论】:

    • 谢谢!这确实让我更接近。但是,它应该遍历每个用户名,然后是所有帐户及其帐号。现在发生的事情是将一个名称与一个帐户进行比较,然后将下一个用户名与下一个帐户进行比较。在进入下一个 AWS 账户之前,需要将一个用户账户与所有 aws 账户进行比较。
    • 这是我现在得到的输出; AWS user name: aadatiya does not exist in AWS account: company-lab AWS user name: abigailcharles does not exist in AWS account: company-bill AWS user name: ab-jenkin does not exist in AWS account: company-stage AWS user name: ab-rrf-dev-pyservice does not exist in AWS account: company-dlab AWS user name: Administrator does not exist in AWS account: company-nonprod AWS user name: adravid does not exist in AWS account: company-prod AWS user name: aelahi does not exist in AWS account: company-govcloud-admin-nonprod
    • 您可以在每个 read 之间使用 &amp;&amp;,以确保在 first 文件结尾处停止循环。
    • @bluethundr 那么你需要一种混合方法;从一个文件读取的外循环和从两个文件(或类似文件)读取的内循环。
    • ... 你可以写read -ru 3 aws_key 而不是read -r aws_key &lt;&amp;3
    猜你喜欢
    • 2011-08-09
    • 1970-01-01
    • 2012-01-02
    • 1970-01-01
    • 2016-08-06
    • 1970-01-01
    相关资源
    最近更新 更多