是的,不是的。删除一个条目是“安全的”,因为该条目在删除后将不存在,但假设在循环迭代时删除它后不会点击该索引是不安全的。
The POSIX spec 不能说:
the following code deletes an entire array:
for (index in array)
delete array[index]
如果这样做可能会跳过索引,那么:
for (index in arrayA) {
if (index in arrayB) {
print "Both:", index
delete arrayA[index]
delete arrayB[index]
}
}
for (index in arrayA)
print "A only:", index
for (index in arrayB)
print "B only:", index
是一个非常常见的习语,用于查找哪些集合值在其中,如果该方法在该上下文中不“安全”,则该方法将不起作用。
但是这并不意味着您可以假设数组索引在被删除后不会被命中在循环期间,因为 awk 是否计算出所有在进入循环之前或执行期间将要访问的数组索引取决于实现。例如,GNU awk 会确定 在进入循环之前它将访问的所有索引,因此您会得到这种行为,其中数组在 delete a[3] 之后缩短了 1 个元素,但删除的索引 3 是仍然在之前被删除的循环中访问过:
$ gawk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; delete a[3]} }'
5 1 a
4 2 b
4 3
4 4 d
4 5 e
但并非所有 awk 都这样做,例如BWK awk/nawk 没有,MacOS/BSD awk 也没有:
$ awk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; delete a[3]} }'
5 2 b
4 4 d
4 5 e
4 1 a
gawk 的行为与上面提到的其他 awk 中的这种行为是等价的:
$ awk 'BEGIN{split("a b c d e",a); for (i in a) b[i];
for (i in b) { print length(a), i, (i in a ? a[i] : x); delete a[3]} }'
5 2 b
4 3
4 4 d
4 5 e
4 1 a
我在上面使用未分配的变量x 而不是"" 来准确描述删除后a[3] 的零或空性质,但在这种情况下实际上并不重要,因为我们正在打印无论如何它都是“”。
因此,无论您使用哪个 awk,一旦退出上述循环,a[3] 就会消失,例如再次使用 GNU awk:
$ gawk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; delete a[3]}
print "---";
for (i in a) {print i, a[i]} }'
5 1 a
4 2 b
4 3
4 4 d
4 5 e
---
1 a
2 b
4 d
5 e
请注意,在上面的脚本中,a[3] 实际上是在第一个循环期间重新创建的,因为当i 是3 时访问a[i],但随后每个索引发生的delete a[3] 再次将其删除。如果我们只在 i 是 1 时进行删除,那么我们会看到 a[3] 存在但在循环后包含零或空值:
$ gawk 'BEGIN{split("a b c d e",a);
for (i in a) {print length(a), i, a[i]; if (i==1) delete a[3]}
print "---";
for (i in a) {print i, a[i]} }'
5 1 a
4 2 b
4 3
5 4 d
5 5 e
---
1 a
2 b
3
4 d
5 e
要了解为什么在开始循环之前预先确定将要访问的索引的 gawk 方法比在循环时尝试动态确定它们更好,请考虑尝试添加 3 个新元素的这段代码到循环内的数组:
$ cat tst.awk
BEGIN {
split("a b c",a)
for (i in a) {
j=i+100
a[j] = "foo" j
print length(a), i, a[i]
}
print "---"
for (i in a) {
print i, a[i]
}
}
使用 gawk,输出和最终结果都是可预测的,并且符合预期:
$ gawk -f tst.awk
4 1 a
5 2 b
6 3 c
---
6 1 a
6 2 b
6 3 c
6 101 foo101
6 102 foo102
6 103 foo103
使用 MacOS/BSD awk 时(忽略顺序,只看数组的长度和索引的值):
$ awk -f tst.awk
4 2 b
5 3 c
6 102 foo102
7 103 foo103
8 202 foo202
9 203 foo203
10 302 foo302
11 1 a
---
11 303 foo303
11 2 b
11 3 c
11 402 foo402
11 101 foo101
11 102 foo102
11 103 foo103
11 202 foo202
11 203 foo203
11 302 foo302
11 1 a
这显然是混乱的,因为它试图访问在循环中添加的索引,但成功有限(可能是由于哈希表中新索引的顺序与先前访问的哈希表条目的顺序),这是幸运的否则我们会陷入无限循环。
要从 MacOS/BSD awk 等中获得有用的结果,您还需要在循环之前将预定索引保存在新数组中,如上所示:
$ cat tst.awk
BEGIN {
split("a b c",a)
for (i in a) {
b[i]
}
for (i in b) {
j=i+100
a[j] = "foo" j
print length(a), i, a[i]
}
print "---"
for (i in a) {
print length(a), i, a[i]
}
}
$ awk -f tst.awk
4 2 b
5 3 c
6 1 a
---
6 2 b
6 3 c
6 101 foo101
6 102 foo102
6 103 foo103
6 1 a
哦,wrt I know we don't have much control on the order in which the array elements are scanned - 使用 GNU awk,您可以通过设置 PROCINFO["sorted_in"] 来精确控制它,请参阅 https://www.gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning。例如:
$ gawk 'BEGIN{split("10 2 bob alf",a);
PROCINFO["sorted_in"]="@ind_str_asc"; for (i in a) print i, a[i]}'
1 10
2 2
3 bob
4 alf
$ gawk 'BEGIN{split("10 2 bob alf",a);
PROCINFO["sorted_in"]="@val_str_asc"; for (i in a) print i, a[i]}'
1 10
2 2
4 alf
3 bob
$ gawk 'BEGIN{split("10 2 bob alf",a);
PROCINFO["sorted_in"]="@val_num_asc"; for (i in a) print i, a[i]}'
4 alf
3 bob
2 2
1 10