我的最终目标是拥有一个脚本来计算所有文件中每个用户名的实例。
用户名是一个字符串,用引号引起来,位于字符串“login”之后。例如,在一个文件中,我可能有:
{"this":"is', {"a":"strange"}, "type":{"of":"object", "but":"please"},
"go":"withit", "login":"username1"}
{"this":"is', {"login":"username2"}, "type":{"of":"object", "but":"please"},
"go":"withit"}
在另一个文件中,我可能有:
{"this":"is', {"a":"strange"}, "type":{"of":"object", "but":"please"},
"go":"withit", "login":"username3"}
{"login":"username1", "please":"gowithit"}
在这种情况下,我想要一个 txt 文件,其中包含一个 dict 对象,其中包含每个用户名在文件中出现的次数:
{"username1": 2, "username2":1, "username3":1}
我读过几本事物到得到我开始,但我似乎无法将其放在一起。我已经对它进行了伪编码,但我无法从这一点继续前进。
我想我需要分两个阶段来做这件事。
1)获取所有用户名的列表
2)统计每个用户名在所有文件中出现的次数。
对于任务 1):
grep 'login:' * | sed 's/^.*: //'
#Except I think this gets everything from the line after 'login', which isn't what I want.
对于任务 2):
for all_usernames_in_file:
stringval = username_read_from_saved_file
cat * | grep -c $stringval > output.txt
任何人都可以从这里拿走它吗?
编辑:
你的意思是我应该这样做:
grep -o 'login":"[^"]*"' /path/to/dir/* | cut -d'"' -f3 | sort | uniq -c | sed '1i{ s/\s*\([0-9]*\)\s*\(.*\)/"\2": \1,/;$a}' > output.txt
编辑2:仍然不起作用。我试图通过了解每个命令的作用来进行诊断。
假设我只是从这一部分开始:
grep -o 'login":"[^"]*"' /path/to/dir/* | cut -d'"' -f3 | sort | uniq -c > myfile.txt
现在,myfile.txt
一片空白。
我认为该命令正在执行以下操作:
grep -o
匹配匹配行的非空部分。
'login":"[^"]*"'
是我们想要 grep 匹配的字符串。在中间,匹配not equal to[^"]
之后的任何字符,并且表示我们想要任何长度的匹配 - 也就是说,用户名的长度并不重要,我们想要引号之间的所有内容。login":"
"
*
|
是一个管道。意思是“然后”
cut -d '"' -f3
login":"
表示使用分隔符 分割返回的行( 后的所有内容) "
,并采用字段 3(即,只是用户名)。
|
是一个管道。意思是“然后”
sort
用户名
|
是一个管道。意思是“然后”
获取唯一的用户名并计算每个用户名出现的次数。
如果我拿那么多,并> myfile.txt
在末尾加上 a,那么我最终应该得到一个 txt 文件,其中包含用户名和每个用户名出现的次数。它的格式不会很好,但它会存在。
为什么我没有得到这样的文件?
注意:我搜索.json.gz
格式化文件有什么关系吗?我已经让脚本在搜索 时可以工作txt
,但不能通过其他格式进行搜索。
答案1
假设您始终将登录名和值放在双引号中,彼此后面没有空格,这是 grep 和计数的构造:
grep -o 'login":"[^"]*"' * | cut -d'"' -f3 | sort | uniq -c
这将生成多次出现的登录列表。
现在我们需要根据它形成您需要的 json 格式。sed
能够为您做到这一点:
| sed '1i{
s/\s*\([0-9]*\)\s*\(.*\)/"\2": \1,/;$a}'
这里sed
将放入{
块的开头和}
结尾,并将uniq
输出更改为您期望的 json 格式。
UPD:最后的命令应该是这样的:
grep -o 'login":"[^"]*"' * | cut -d'"' -f3 | sort | uniq -c | sed '1i{
s/\s*\([0-9]*\)\s*\(.*\)/"\2": \1,/;$a}' > file.txt
答案2
获取所有用户名,即与某个关联的所有字符串login
要从 a格式良好的 JSON 文档,在不知道文档结构的情况下:
jq -r '..|select(.login?).login' file.json
将其应用于多个 JSON 文件,并对结果进行排序和计数:
jq -r '..|select(.login?).login' *.json | sort | uniq -c
这里使用的表达jq
是
..
:递归遍历所有键和值。select(.login?)
:选择遇到的包含键的对象login
。.login
:获取该键的值。
你想要的字典,基于上面的jq
表达式:
jq -sr '[..|select(.login?).login]|group_by(.)|map({key:.[0],value:length})|from_entries' *.json
测试:
$ cat file.json
{"this":"is", "A":{"login":"username2"}, "type":{"of":"object", "but":"please"},
"go":"withit", "login":"me"}
$ jq -sr '[..|select(.login?).login]|group_by(.)|map({key:.[0],value:length})|from_entries' file.json
{
"me": 1,
"username2": 1
}
给它相同的文件两次:
$ jq -sr '[..|select(.login?).login]|group_by(.)|map({key:.[0],value:length})|from_entries' file.json f
ile.json
{
"me": 2,
"username2": 2
}
使用jq
with-c
可获得单行紧凑输出。
对于我们的示例文件,jq -sr '[..|select(.login?).login]' file.json
将产生
[
"me",
"username2"
]
通过这个group_by(.)
给出
[
[
"me"
],
[
"username2"
]
]
该map({key:.[0],value:length})
部分给出
[
{
"key": "me",
"value": 1
},
{
"key": "username2",
"value": 1
}
]
最后from_entries
给出最终结果。
答案3
如何使用以正则表达式匹配为键的 Perl 哈希,您可以使用 JSON 模块进行转换:
$ perl -MJSON -lne '$h{$1}++ for /(?<="login":")(.*?)(?=")/g }{ print encode_json \%h' file1 file2
{"username3":1,"username2":1,"username1":2}
答案4
@rush 使用sed
在我的 shell 中不起作用,所以我这样做了
grep -Poh '(?<=login":")[^"]*' json* | sort | uniq -c | awk -v OFS=': ' 'BEGIN{print "{"}{print $2, $1}END{print"}"}' | sed -E 's/([0-9])$/\1,/g;s/:/\":/g;s/^([^{}])/\"\1/g'
sed
如果您的 shell 允许您转义并在语句"
中打印它们,则可以修改倍数。awk
grep -Poh '(?<=login":")[^"]*' json* | sort | uniq -c | awk -v OFS=': ' 'BEGIN{print "{"}{print \"$2\", $1}END{print"}"}' | sed -E 's/([0-9])$/\1,/g'
我的外壳被第二个脚本awk
噎住了。\"
不知道为什么,但我确信有人会告诉我。
我也尝试过,jq
但是它被 json 文件卡住了。似乎有语法错误
"this":"is' #is written so I edited these to
"this":"is"
也不jq
喜欢这个结构
{"a":"strange"} # so I also edited these to
b: {"a":"strange"}
如果原始文件应该与所做的编辑一致,那么就jq
可以工作
jq '.login' json* | sort | uniq -c | awk -v OFS=': ' 'BEGIN{print "{"}{print $2, $1}END{print"}"}' | sed -E 's/([0-9])$/\1,/g'