将循环内容捕获到变量中

将循环内容捕获到变量中

我有一个 for 循环,它将所有内容输出到/etc/trueuserownersdu。我试图弄清楚如何获取此 for 循环的输出并将其全部放入变量中,而无需将其放入数组中。

for i in $(grep dook /etc/trueuserowners | cut -d : -f 1); do du -sh /home/$i; done | sort -nr

这样做的主要目的是我可以获取的输出du -sk,将其放入变量中pp计算人类可读的形式并在最后得到总数。

我试图将其缩减为一行,但如果绝对需要,可以/将转换为 bash 脚本。

它正在具有 cPanel 用户的服务器上运行。

的内容/etc/trueuserowners看起来像这样。

user1: reseller
user2: reseller
user3: reseller

这将是我正在使用的输出示例,如下所示。

for i in $(grep root /etc/trueuserowners | cut -d : -f 1); do du -sh /home/$i; done | sort -nr
992K    /home/demoabusecenter
820K    /home/lakshdljkashdlkj
151M    /home/hyg8
58M /home/bugsabusecenter
21G /home/esportsoverlay
3.3G    /home/yourabuse
2.8M    /home/kf30ls08f2k0
1.4M    /home/perm2term
1.2M    /home/justcheck
1.1M    /home/myinfo1

这里的总体目标是让它提供当前的输出并给我每个用户的总数。

我认为我找到了解决方案,但我不知道如何将该输出放入变量中。

这是我尝试时发生的情况。

for i in $(grep root /etc/trueuserowners | cut -d : -f 1); do p=$(du -sh /home/$i); done | sort -nr | echo $p
20981044 /home/esportsoverlay

由于某种原因,它将空间的输出转换为 MB。这很好,因为我可以将其转换为 GB。我只是无法让它显示所有内容。它只向我显示 for 循环的最后一个用户输出。

答案1

答案将会很长,但我要解决的只是很多部分,所以请继续听我说。

好吧,首先要说的是,你迭代grep输出的方法不太正确。这种for i in $(); do ...done方法在包含前导空格的行上会中断(因为单词拆分),一般不推荐(见)。

但是,通常的做法是command | while IFS=<word separator> read -r variable; do...done(作为额外的好处,它可以在类似 Bourne 的 shell 之间移植)。另外,我看到你正在使用 来cut -d : -f 1从每行冒号分隔的列表中获取第一个项目。我们可以使用IFS=":"then 来删除cut部分。因此,你的原始命令被转换为:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else 
do 
    du -sh /home/$i
done | sort -nr

first_word变量显然只包含第一个字段,而未使用的字段everything_else将包含……其他所有内容。

请注意,我还引用了'dook'部分;尽管grep理解第一个项目。尽管grep理解第一个非选项项目为 PATTERN,但用单引号保护模式确实是一种很好的做法,因为如果您使用*或 shell 使用的一些其他正则表达式模式,这将无意中执行文件名扩展bash,并尝试读取当前工作目录中的文件。请参阅这个请参阅详细示例。

接下来,让我们讨论数组。我们当然可以添加数组中的新项 使用while上面我展示的循环和+=运算符:

$ declare -a my_array
$ while IFS=":" read -r name null; do my_array+=("$name"); done < /etc/passwd
$ echo "${my_array[0]}"
root

如果您想要稍后处理提取的项目,这会很方便。但是,考虑到您在那里有管道,一旦运行的子 shellwhile退出,变量就会消失。请参阅我的老问题关于那个。

我建议处理所有内容,while loop并用于du -sb计算精度。因此,您可以这样做:

# read what grep finds line by line and print total once there's no more lines from grep
grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else || { echo "$total"; break;} 
do 
    user_usage=$( du -sb /home/"$first_word" | awk '{print $1}' )
    # output the usage for current user
    printf "%s\t/home/%s\n" "$user_usage"  "$first_word"
    total=$(($total+$user_usage))
done 

注意我是如何使用的|| { echo "$total"; break;}。一旦没有内容可读取stdin(在本例中来自管道),read命令将返回退出状态 1,因此当read返回 1 时,我们就知道它已完成读取和处理,并且我们可以输出我们计算的总使用量。

至于输出人类可读的数据,我们可以使用numfmt其他一些实用程序. 类似的东西numfmt --to=iec-i --suffix=B $user_usage就足够了。

总的来说,如果我们将变量名修剪成较短的,这可以用作一行代码,但一行代码并没有什么特别的优势。只要尽可能正确地做事,不要担心代码长度。

综合起来,完整的解决方案应该是:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r username trash || { printf "%s\ttotal\n" $( numfmt --to=iec-i --suffix=B "$total"); break;} 
do 
    # get usage in bytes
    user_usage=$( du -sb /home/"$username" | awk '{print $1}' )
    # get human-readable
    usage_human_readable=$( numfmt --to=iec-i --suffix=B "$user_usage" )
    # output the usage for current user
    printf "%s\t/home/%s\n" "$usage_human_readable"  "$username"
    total=$(($total+$user_usage))
done 

相关内容