ksh 中一个键的多个值
Multiple values for a key in ksh
我正在尝试读取成对的文件,如下所示:
V1#K1.@
V2#K1.@
V3#K2.@,V4#K1.@,V5#K2
V1#K3.@
我的目标是在删除 '@'
之后将其存储在 key<=>pairs
中,以 #
作为分隔符 Value 放在 #
之前,Keys 在 [=13= 之后] 在示例文件中
中提到的答案无法执行。
所以我在 ksh 中按以下方式尝试了它:
#!/usr/bin/ksh
typeset -A arr
while IFS= read -r line;do
STRIPPED=`echo $line|sed 's/.@//g'`
OIFS="$IFS"
IFS=','
read -A TOKENS <<< "${STRIPPED}"
IFS="$OIFS"
for key in ${TOKENS[@]};do
echo "Token is $key"
arr[${i##*#}]=${i%%#*}
echo "Key: ${key##*#}, Value: ${arr[${key##*#}]}"
done
done <MYFILE
# Printing key and its values
for i in ${!arr[@]};do
echo "key: ${i}, value: ${arr[$i]}"
done
但这会覆盖键的先前值。它不考虑一个键的多个值。
有没有办法在 ksh 中做到这一点(不是 bash)?
我会这样做,它将多个值存储为 comma-separated 字符串
#!/usr/bin/env ksh
# The `exec` line tells ksh to read from MYFILE _if_ stdin has _not_ been redirected
# This allows you to do:
# ./script.ksh
# ./script.ksh < some_other_file
# some_process | ./script.ksh
[[ -t 0 ]] && exec 0<MYFILE
typeset -A arr
while IFS= read -r line; do
# greatly simplified tokenization
IFS=',' read -rA tokens <<< "${line//.*/}"
for t in "${tokens[@]}"; do
key=${t%#*}
val=${t#*#}
[[ -n ${arr[$key]} ]] && arr[$key]+=,
arr[$key]+=$val
done
done
# Printing key and its values
for i in "${!arr[@]}"; do
echo "key: ${i}, value: ${arr[$i]}"
done
输出
key: V1, value: K1,K3
key: V2, value: K1
key: V3, value: K2
假设:
- 输入数据的格式与问题中显示的完全相同(即,无需担心 other/extraneous 文本)
- 示例输入的第 3 行在第 3 个 attribute/value 对
的末尾缺少“.@”
- 为了演示重复处理,我只复制最后一行几次
- 该问题没有所需输出的示例,因此我将使用 glenn 的示例输出
- 没有明确提及任何排序首选项(对于输出),因此我将跳过尝试在这一点上进行任何类型的排序
输入文件:
$ cat kdat
V1#K1.@
V2#K1.@
V3#K2.@,V4#K1.@,V5#K2.@
V1#K3.@
V1#K3.@
V1#K3.@
一个基于sed
和awk
的解决方案(在bash
和ksh
中都可用),我们使用attribute/value对作为索引一个二维数组。通过指定一个任意值(在本例中为“1”)作为数组值,我们可以消除重复值。
- 我们第一次看到(新)attribute/value 对时,我们创建数组元素
- 下次我们看到(相同的)attribute/value 对时,我们只需覆盖数组元素
- 当我们处理完输入后,我们发现每个 attribute/value 对都与一个数组元素相关联(即没有重复)
现在实际代码:
$ sed 's/,/\n/g;s/.@//g' kdat | awk -F"#" '
{ myarray[][]=1 }
END { for (i in myarray)
{ delim=""
printf "key: %s, value: ",i
for (j in myarray[i])
{ printf "%s%s",delim,j
delim=","
}
printf "\n"
}
}
'
key: V1, value: K1,K3
key: V2, value: K1
key: V3, value: K2
key: V4, value: K1
key: V5, value: K2
其中:
sed ...
:用回车符 return 替换逗号(每个 attribute/value 对在单独的一行上;此 awk
解决方案假定一对 attribute/value每行);删除“.@”
awk -F"#" ...
:使用“#”作为分隔我们的属性($1)和值($2)对的输入分隔符
myarray[][]=1
: create/overwrite array($1,$2) with '1';这是丢弃重复项的地方
for / printf
:遍历数组索引,使用 printf
漂亮地打印我们的输出
我正在尝试读取成对的文件,如下所示:
V1#K1.@
V2#K1.@
V3#K2.@,V4#K1.@,V5#K2
V1#K3.@
我的目标是在删除 '@'
之后将其存储在 key<=>pairs
中,以 #
作为分隔符 Value 放在 #
之前,Keys 在 [=13= 之后] 在示例文件中
#!/usr/bin/ksh
typeset -A arr
while IFS= read -r line;do
STRIPPED=`echo $line|sed 's/.@//g'`
OIFS="$IFS"
IFS=','
read -A TOKENS <<< "${STRIPPED}"
IFS="$OIFS"
for key in ${TOKENS[@]};do
echo "Token is $key"
arr[${i##*#}]=${i%%#*}
echo "Key: ${key##*#}, Value: ${arr[${key##*#}]}"
done
done <MYFILE
# Printing key and its values
for i in ${!arr[@]};do
echo "key: ${i}, value: ${arr[$i]}"
done
但这会覆盖键的先前值。它不考虑一个键的多个值。 有没有办法在 ksh 中做到这一点(不是 bash)?
我会这样做,它将多个值存储为 comma-separated 字符串
#!/usr/bin/env ksh
# The `exec` line tells ksh to read from MYFILE _if_ stdin has _not_ been redirected
# This allows you to do:
# ./script.ksh
# ./script.ksh < some_other_file
# some_process | ./script.ksh
[[ -t 0 ]] && exec 0<MYFILE
typeset -A arr
while IFS= read -r line; do
# greatly simplified tokenization
IFS=',' read -rA tokens <<< "${line//.*/}"
for t in "${tokens[@]}"; do
key=${t%#*}
val=${t#*#}
[[ -n ${arr[$key]} ]] && arr[$key]+=,
arr[$key]+=$val
done
done
# Printing key and its values
for i in "${!arr[@]}"; do
echo "key: ${i}, value: ${arr[$i]}"
done
输出
key: V1, value: K1,K3
key: V2, value: K1
key: V3, value: K2
假设:
- 输入数据的格式与问题中显示的完全相同(即,无需担心 other/extraneous 文本)
- 示例输入的第 3 行在第 3 个 attribute/value 对 的末尾缺少“.@”
- 为了演示重复处理,我只复制最后一行几次
- 该问题没有所需输出的示例,因此我将使用 glenn 的示例输出
- 没有明确提及任何排序首选项(对于输出),因此我将跳过尝试在这一点上进行任何类型的排序
输入文件:
$ cat kdat
V1#K1.@
V2#K1.@
V3#K2.@,V4#K1.@,V5#K2.@
V1#K3.@
V1#K3.@
V1#K3.@
一个基于sed
和awk
的解决方案(在bash
和ksh
中都可用),我们使用attribute/value对作为索引一个二维数组。通过指定一个任意值(在本例中为“1”)作为数组值,我们可以消除重复值。
- 我们第一次看到(新)attribute/value 对时,我们创建数组元素
- 下次我们看到(相同的)attribute/value 对时,我们只需覆盖数组元素
- 当我们处理完输入后,我们发现每个 attribute/value 对都与一个数组元素相关联(即没有重复)
现在实际代码:
$ sed 's/,/\n/g;s/.@//g' kdat | awk -F"#" '
{ myarray[][]=1 }
END { for (i in myarray)
{ delim=""
printf "key: %s, value: ",i
for (j in myarray[i])
{ printf "%s%s",delim,j
delim=","
}
printf "\n"
}
}
'
key: V1, value: K1,K3
key: V2, value: K1
key: V3, value: K2
key: V4, value: K1
key: V5, value: K2
其中:
sed ...
:用回车符 return 替换逗号(每个 attribute/value 对在单独的一行上;此awk
解决方案假定一对 attribute/value每行);删除“.@”awk -F"#" ...
:使用“#”作为分隔我们的属性($1)和值($2)对的输入分隔符myarray[][]=1
: create/overwrite array($1,$2) with '1';这是丢弃重复项的地方for / printf
:遍历数组索引,使用printf
漂亮地打印我们的输出