追加到数组并保留循环外的变量[重复]

时间:2022-05-27 00:23:51

This question already has an answer here:

这个问题在这里已有答案:

Please note that this is a pseudo code to illustrate the problem. I just used ls for example and I'm aware that I shouldn't parse the output of ls. The original script is used to iterate through AWS S3 buckets.

请注意,这是一个伪代码来说明问题。我只是用ls为例,我知道我不应该解析ls的输出。原始脚本用于迭代AWS S3存储桶。

Directory structure:

# tree
.
├── dir1
│   ├── d1_file1
│   ├── d1_file2
│
└── dir2
    ├── d2_file1
    ├── d2_file2

2 directories, 4 files

Desired outcome:

# bash tmp.sh
dir1 has 2 files(s)
dir1 files: d1_file1 d1_file2
dir2 has 2 files(s)
dir2 files: d2_file1 d2_file2
Warnings: 2

# Command grouping - file are listed correctly however WARNING variable is lost due to sub-shell created by pipe.

#命令分组 - 文件正确列出但是由于管道创建的子shell导致WARNING变量丢失。

#!/bin/bash

WARNING=0

list=(
dir1
dir2
)

for dir in ${list[@]}; do
    ls -1 ./tmp/${dir} |
    {
    while read -a file; do
       files+=(${file})
    done
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
    if [ ${#files[@]} -gt 1 ]; then
        (( WARNING++ ))
    fi
}
done
echo "Warnings: ${WARNING}"

----- Output -----

dir1 has 2 files(s)
dir1 files: d1_file1 d1_file2
dir2 has 2 files(s)
dir2 files: d2_file1 d2_file2
Warnings: 0

# Process Substitution - WARNING variable is preserved outside of the loop but output is incorrect.

#进程替换 - WARNING变量在循环外保留但输出不正确。

#!/bin/bash

WARNING=0

list=(
dir1
dir2
)

for dir in ${list[@]}; do
    while read -a file; do
       files+=(${file})
    done  < <(ls -1 ./tmp/${dir})
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
    if [ ${#files[@]} -gt 1 ]; then
        (( WARNING++ ))
    fi
done
echo "Warnings: ${WARNING}"

----- Output -----

dir1 has 2 files(s)
dir1 files: d1_file1 d1_file2
dir2 has 4 files(s)
dir2 files: d1_file1 d1_file2 d2_file1 d2_file2
Warnings: 2

# Here Document - here the output and number of warnings is incorrect

#Here Document - 此处输出和警告数量不正确

#!/bin/bash

WARNING=0

list=(
dir1
dir2
)

for dir in ${list[@]}; do
    while read -a file; do
       files+=(${file})
    done  <<<$(ls -1 ./tmp/${dir})
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
    if [ ${#files[@]} -gt 1 ]; then
        (( WARNING++ ))
    fi
done
echo "Warnings: ${WARNING}"

----- Output -----

dir1 has 1 files(s)
dir1 files: d1_file1
dir2 has 2 files(s)
dir2 files: d1_file1 d2_file1
Warnings: 1

Could you please advise what approach I should use in order to get the desired outcome and what method is actually recommended in my case.

你能否告诉我应采用什么方法来获得理想的结果,以及在我的案例中实际推荐的方法。

1 个解决方案

#1


Reasons for not working how you want

不按你想要的方式工作的原因

Command grouping

As you said the warning is lost due to the sub-shell, but also so is the files array giving the appearance of that part working

正如您所说,由于子shell而导致警告丢失,但文件阵列也是如此,这使得该部件的外观起作用

Process Substitution

Unlike the subshell, the files array is not reset and so each subsequent loop/directory still contains files from the previous one.

与子shell不同,files数组不会重置,因此每个后续的循环/目录仍然包含前一个的文件。

Here Document

Adds to the array incorrectly, only adding a single file at a time due to the way ls is output from the here document.

不正确地添加到数组,由于从here文档输出ls的方式,一次只添加一个文件。


Solution

You can use process substitution and just reset the array between loops like

您可以使用进程替换,只需在循环之间重置数组

#!/bin/bash

WARNING=0

list=(
dir1
dir2
)

for dir in ${list[@]}; do
    {
    while read -a file; do
       files+=(${file})
    done < <(ls -1 tmp/${dir})
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
    if [ ${#files[@]} -gt 1 ]; then
        (( WARNING++ ))
    fi
        unset files   ## THIS LINE
}
done
echo "Warnings: ${WARNING}"

This is assuming you don't actually want the file array, and just want to output them

这假设您实际上并不想要文件数组,只想输出它们

P.s Don't parse ls ;)

P.s不要解析ls;)

#1


Reasons for not working how you want

不按你想要的方式工作的原因

Command grouping

As you said the warning is lost due to the sub-shell, but also so is the files array giving the appearance of that part working

正如您所说,由于子shell而导致警告丢失,但文件阵列也是如此,这使得该部件的外观起作用

Process Substitution

Unlike the subshell, the files array is not reset and so each subsequent loop/directory still contains files from the previous one.

与子shell不同,files数组不会重置,因此每个后续的循环/目录仍然包含前一个的文件。

Here Document

Adds to the array incorrectly, only adding a single file at a time due to the way ls is output from the here document.

不正确地添加到数组,由于从here文档输出ls的方式,一次只添加一个文件。


Solution

You can use process substitution and just reset the array between loops like

您可以使用进程替换,只需在循环之间重置数组

#!/bin/bash

WARNING=0

list=(
dir1
dir2
)

for dir in ${list[@]}; do
    {
    while read -a file; do
       files+=(${file})
    done < <(ls -1 tmp/${dir})
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
    if [ ${#files[@]} -gt 1 ]; then
        (( WARNING++ ))
    fi
        unset files   ## THIS LINE
}
done
echo "Warnings: ${WARNING}"

This is assuming you don't actually want the file array, and just want to output them

这假设您实际上并不想要文件数组,只想输出它们

P.s Don't parse ls ;)

P.s不要解析ls;)