我正在编写一个脚本,将命令的结果发送到输出数组。它涉及检查服务器的日志并检索其大小,但是在某些情况下,服务器具有故障转移主机。在这些情况下,我需要脚本来检查两台主机并返回值。我遇到的问题是,如果任何输出数组元素为空(意味着服务器中不存在要检查的文件,仅在其各自的回显显示中返回一个空格),则会导致数组索引发生变化。这意味着辅助主机阵列集中应包含的内容会撞到主要主机阵列集中。相反,我希望将这些空索引存储为 0 作为占位符,以防止发生移位。这是索引偏移明显的地方:
echo The primary overall log value is ${output[0]}.
echo The primary obs log value is ${output[2]}.
echo The primary tracks log value is ${output[4]}.
echo
echo The secondary overall log value is ${output[6]}.
echo The secondary overall log value is ${output[8]}.
echo The secondary overall log value is ${output[10]}.
例如,如果输出[2]和[4]为空,则输出[6]将向上移动到与输出[2]相对应的行。我尝试过这个解决方案,但没有运气:
s=0
for x in "${output[@]}"
do(
x=
if [[ -z $x ]];
then(
x=0
echo $x)
else echo
fi
s=s+1)
done
所有这一切都是在输出回波发生之前吐出零,并且不会针对任何索引移位进行调整。
注意:这是输出数组的来源,以及在其下方进行修复的尝试:
for h in "${host[@]}"
do
for path in "${paths[@]}"
do
output+=( $(ssh $h du -sh $path) )
for x in "${!output[@]}";
do(
if [[ -z "${output[$x]}" ]];
then output[$x]=0;
fi;
)
done
done
done
其中主机和路径是已经定义的数组。我还注意到,该脚本在只有一台主机可供访问的情况下运行良好,并且那里的数组索引没有问题。
答案1
例如,如果输出[2]和[4]为空,则输出[6]将向上移动到与输出[2]相对应的行。
在这种情况下,您实际上并没有用空值来代替 2 和 4,只是您的值比您预期的要少。
考虑数组分配:
array1=(a b c d)
array2=(A D)
第一个设置array1[0]
为a
、array1[1]
tob
等。第二个设置array2[0]
为A
和array2[1]
to D
。无法知道A
和之间是否应该有空值D
。他们需要明确地出现在作业中:
array2=(A "" "" D)
您可能以其他方式填充数组output
,但由于您没有显示它是如何完成的,因此无法对其进行评论。您可能遇到与上面类似的情况:“空”值根本不被填充数组的任何值视为值output
。
如果您从扩展中分配给数组并依赖分词,则无法真正避免这种情况。 (即使IFS
只包含换行符,它也会将连续的空换行符折叠为一个,删除空行)。如果您使用mapfile
,则默认情况下空行应显示为数组元素。
在任何情况下,将空数组元素置零的循环都不起作用,因为您根本没有在循环内分配给数组,即使分配了,循环体也会在子shell中运行(如括号(...)
),因此任何赋值都不会发生在循环体之外。
您不能真正使用 afor x in "${output[@]}"
来有效地修改数组元素,因为x
仅获取数组值的副本,修改它不会更改原始值。您需要循环数组索引才能指向数组:
somearray=(1 "" "" 4)
for i in "${!somearray[@]}"; do
if [[ -z "${somearray[$i]}" ]]; then
somearray[$i]=0;
fi;
done
echo "${somearray[@]}"
印刷1 0 0 4
。
在您添加的示例代码中,分配output+=( $(ssh $h du -sh $path) )
不会添加任何空元素。缺失的路径将从数组中省略。首先考虑一下,du $path
如果路径不存在,则不会打印任何内容。 (除了错误之外,它会发送到 stderr,并且不会被命令替换捕获。)此外,即使它确实打印了空行(或空格/制表符),分词也会删除连续的空格。
尝试例如array=( $( printf "foo\n\nbar\n" ) )
,它创建一个包含两个元素的数组。
此外,替换循环中的括号启动一个子shell,因此对数组的任何修改只会在该子shell内生效。一般来说,你不想要用括号将 shell 命令分组,除非您知道您特别需要一个子 shell。
您可以在命令替换 ( "$(ssh ... du)"
) 两边加上引号,以确保您得到的正是一个字符串,但您可能希望将大小与路径名分开。
尝试这样的方法将路径及其大小收集到单个数组中:
for host in "${hosts[@]}"; do
for path in "${paths[@]}"; do
output="$(ssh "$host" du -sh "$path")"
size="${output%%$'\t'*}" # needs Bash/ksh/zsh
size="${size:-0}"
sizes+=( "$size" "$host:$path" )
done
done
现在,每个偶数数组元素都包含一个大小,每个奇数数组元素都包含相应的主机名和路径,有点像分割 的输出du
会导致的结果。 的两个参数扩展output
和size
删除制表符后的所有内容,然后将大小替换为如果为空则为零。
或者,你可以构建关联数组,由主机+路径索引:
declare -A array
...
array["$host:$path"]=$size