我想做一些简单的事情,但我不确定如何在这里实现我的目标。
w
我正在尝试提取控制台上的命令给出的 USER、TTY 和 FROM 值。在 bash 中,我尝试获取此输出并将这些值放入多维数组(或只是带有空格分隔符的数组)中。
#!/bin/bash
w|awk '{if(NR > 2) print $1,$2,$3}' | while read line
do
USERS+=("$line")
echo ${#USERS[@]}
done
echo ${#USERS[@]}
我已经找到了在单个数组中逐行读取值的方法,但是我似乎无法将 USERS 数组值移出 while 循环的范围。它打印值 1,2,3,4,然后在循环后打印 0。我读到的每个例子都很好地使用了范围之外的变量,但我似乎不能。
答案1
您的主要问题是管道中的最后一个命令在子 shell 中运行,就像管道中的所有其他命令一样。大多数 shell 都是这种情况。 ATT ksh 和 zsh 是例外:它们在父 shell 中运行管道的最后一个命令。
从 bash 4.2 开始,您可以通过设置来告诉 bash 表现得像 ksh 和 zshlastpipe
选项。
#!/bin/bash
USERS=()
shopt -s lastpipe
w | awk '{if(NR > 2) print $1,$2,$3}' | while read line; do
USERS+=("$line")
done
echo ${#USERS[@]}
或者,您可以使用流程替代而不是管道,以便read
命令在主 shell 进程中运行。
#!/bin/bash
USERS=()
while read line; do
USERS+=("$line")
done < <(w | awk '{if(NR > 2) print $1,$2,$3}')
echo ${#USERS[@]}
或者,您可以使用可移植的方法,该方法适用于没有进程替换或 ksh/zsh 行为的 shell,例如 Bourne、dash 和 pdksh。 (对于数组,您仍然需要 (pd)ksh、bash 或 zsh。)在管道内运行需要来自管道的数据的所有内容。
#!/bin/bash
USERS=()
shopt -s lastpipe
w | awk '{if(NR > 2) print $1,$2,$3}' | {
while read line; do
USERS+=("$line")
done
echo ${#USERS[@]}
}
答案2
您shopt -s lastpipe
可以使用管道的最后一个命令进入当前的 shell 环境。这解决了你的问题。我想这个功能并不总是在 bash 中存在,所以如果你想要广泛兼容的代码,请避免使用它。
兼容的替代方案:
export_array="$(w | awk '{if(NR > 2) print $1,$2,$3}' |
{ USERS=(); while read line; do
USERS[]="$line"
done
declare -p USERS; } )"
eval "$export_array"
答案3
Bash 数组是一维的。如果您想为每一行保存有序的单独值,一种解决方案是使用关联数组。一个粗略的例子:
还要小心那些大写变量名称,因为它们可能与环境变量冲突。
#!/bin/bash
declare -i i=0 j=0
declare -A w
while read -r user tty from _;do
((++i > 2)) || continue
w["$j.user"]="$user"
w["$j.tty"]="$tty"
w["$j.from"]="$from"
((++j))
done < <(w)
for ((i = 0; i < j; ++i)); do
printf "entry %-2d {\n %-5s: %s\n %-5s: %s\n %-5s: %s\n}\n" \
"$i" \
"user" "${w[$i.user]}" \
"tty" "${w[$i.tty]}" \
"from" "${w[$i.from]}"
done
答案4
对于 bash 数组中的存储,使用空格以外的分隔符通常更简单。
readarray -s2 -t my_w_array < <(w | awk '{ print $1":"$2":"$3 }')
然后您可以在打印时将其拆分,例如:
printf '%s\n' "${my_w_array[@]//:/ }"