我正在解析一个日志文件,并为每个用户创建具有行号和最后一个字段的关联数组(登录的总时间)。日志文件的行如下所示:
jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (00:58)
jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (04:26)
如果第一个字段(jww3321)是数组名称,并且数组中的第一个条目将是(1,00:58),则下一个将是(2,(下次用户))。为了获得正确的密钥,我需要保存列表的长度,并在将下一个值添加到用户数组时添加一个。到目前为止我的代码看起来像这样:
cat lastinfo.txt | while read line
do
uname=`echo "$line" | awk '{print $1;}'`
count=`echo "${#$uname[@]}"`
echo "$count"
done
我尝试过使用间接引用,但我遇到了这个错误:
l8t1: line 7: ${#$uname[@]}: bad substitution
有什么建议吗?
答案 0 :(得分:4)
我不确定我是否理解你正在尝试做什么,特别是“关联”部分(我无法看到使用关联数组的位置),但是这段代码做了我理解你想要的东西做:
#!/bin/bash
while IFS=" " read user time; do
eval "item=\${#$user[@]} ; $user[\$item]=\(\$((\$item + 1)),$time\)"
[[ "${arraynames[@]}" =~ $user ]] || arraynames[${#arraynames[@]}]=$user
done< <(sed -r 's/^ *([[:alnum:]]*) .*\((.*)\)$/\1 \2/')
for arrayname in ${arraynames[@]}; do
eval "array=(\${$arrayname[@]})"
echo "$arrayname has ${#array[@]} entries:"
for item in ${!array[@]}; do
echo "$arrayname[$item] = ${array[$item]}"
done
echo
done
它从stdin读取。我用这样的示例文件测试了它:
jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (00:58) jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (04:26) jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (01:58) jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (05:26)
输出:
jww3321 has 2 entries: jww3321[0] = (1,00:58) jww3321[1] = (2,01:58) jpd8635 has 2 entries: jpd8635[0] = (1,04:26) jpd8635[1] = (2,05:26)
请注意,仅使用标准的整数索引数组。在Bash中,截至目前,左侧的间接数组引用总是涉及使用eval
(uuuuuuhhhhh,幽灵般的声音),在右侧,您可以使用${!}
离开替换和命令替换$()
。
使用eval的经验法则:在eval
时逃避您想要展开的内容,并且在 eval
时间之前不要逃避您想要展开的。如果您对最终被评估的内容有疑问,请复制该行并更改eval
echo
。
编辑:回答sarnold的评论,这是一种没有评估的方法:
#!/bin/bash
while IFS=" " read user time; do
array=$user[@] array=( ${!array} ) item=${#array[@]}
read $user[$item] <<< "\($(($item + 1)),$time\)"
[[ "${arraynames[@]}" =~ $user ]] || arraynames[${#arraynames[@]}]=$user
done< <(sed -r 's/^ *([[:alnum:]]*) .*\((.*)\)$/\1 \2/')
for arrayname in ${arraynames[@]}; do
array=$arrayname[@] array=( ${!array} )
echo "$arrayname has ${#array[@]} entries:"
for item in ${!array[@]}; do
echo "$arrayname[$item] = ${array[$item]}"
done
echo
done
答案 1 :(得分:2)
您不是在创建关联数组。该错误与${#$uname[@]}
的语法有关:删除第二个美元符号。
答案 2 :(得分:2)
在bash
内,您可以使用eval
:
eval count=`echo "$\{#$uname[@]\}"`
RESP。
eval count="$\{#$uname[@]\}"
答案 3 :(得分:1)
我喜欢bash(1)
,我认为这对于“小”任务来说是公平的。我经常对在狭小空间里完成多少工作印象深刻。但我认为其他语言可以提供更友好的数据结构。十年前,我会在不考虑两次的情况下使用perl(1)
,但我已经开始不喜欢将哈希存储为其他哈希值的语法。 Python也很简单,但我现在比Python更了解Ruby,所以这里有类似于你正在做的事情:
#!/usr/bin/ruby -w
users = Hash.new() do |hash, key|
hash[key] = Array.new()
end
lineno = 0
while(line = DATA.gets) do
lineno+=1
username, _ptr, _loc, _dow, _mon, _date, _in, _min, _out, time =
line.split()
u = users[username]
minutes = 60 * Integer(time[1..2]) + Integer(time[4..5])
u << [lineno, minutes]
end
users.each() do |user, list|
total = list.inject(0) { |sum, entry| sum + entry[1] }
puts "#{user} was on #{list.length} times for a total of #{total} minutes"
end
__END__
jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (00:58)
jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (04:26)
jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (00:58)
jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (04:26)
jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (00:58)
jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (04:26)
__END__
(以及相应的DATA
)只是为了使其成为独立的
例。如果您选择使用此功能,请将DATA
替换为STDIN
和
删除__END__
及其后的所有内容。
由于我主要是在C中思考,这可能不是最惯用的Ruby
例如,但它确实演示了散列(关联数组)如何具有
每个键的数组(遗憾的是它比它复杂得多)
be),显示如何附加到数组(u << ...
),显示一些简单
数学,显示对哈希(users.each() do
...
)的一些简单迭代,甚至使用一些higher order
functions(list.inject(0) { .. }
)
计算sum
。是的,总和可以用更常见的方式计算
循环构造,但有一些关于“做这个”的选择
对该列表的所有元素进行操作“使其成为一个简单的构造
选择。
当然,我不知道你真正做了什么
last(1)
命令,但ruby(1)
似乎比。bash(1)
更容易
相应的{{1}}脚本将是。 (我希望看到它,在
结束,仅仅是为了我自己的教育。)