我有一个 for 循环,它将所有内容输出到/etc/trueuserowners
中du
。我试图弄清楚如何获取此 for 循环的输出并将其全部放入变量中,而无需将其放入数组中。
for i in $(grep dook /etc/trueuserowners | cut -d : -f 1); do du -sh /home/$i; done | sort -nr
这样做的主要目的是我可以获取的输出du -sk
,将其放入变量中p
并p
计算人类可读的形式并在最后得到总数。
我试图将其缩减为一行,但如果绝对需要,可以/将转换为 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