解析 Rbox 执行命令返回的文件统计信息
Parsing file stats returned by Rbox execute command
我是 Ruby 编程的新手,现在我正在使用 Rbox/Rye 编写一些测试自动化 ruby 脚本。
作为测试的一部分,客户将一个 +10GB 的文件上传到 ftp 主机。
该脚本每分钟检查文件是否仍在上传、上传完成或上传失败。
对于测试,我使用 Rbox/Rye 执行 shell 命令来查找文件统计信息:
def check_file_upload()
rbox = Rye::Box.new("#{@host}")
rbox.disable_safe_mode
result = rbox.execute "stat #{@file}"
end
stats = file.check_file_upload()
puts stats
"puts" 正确打印文件统计信息。格式与我在 linux 主机上执行 "stat file" 命令相同。
现在,有没有一种方法可以让我真正解析
返回的值
check_file_upload()
方法。我可以使用 Ruby 的内置正则表达式,或者 grep/awk,从返回的文件统计信息中选择特定信息吗?
以下是文件统计:
File: `/home/user/file_name.dmp'
Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file
Device: 802h/2050d Inode: 57442314 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user)
Access: 2015-01-06 11:32:17.000000000 -0700
Modify: 2015-01-06 11:38:59.000000000 -0700
Change: 2015-01-06 11:38:59.000000000 -0700
更具体地说,我想选择文件大小、访问、修改和更改值
是的,这对于正则表达式来说是一项简单的任务。这是一个 class,它从 "stat" 字符串中解析有趣的位并将它们公开为属性:
class FileStatInfo
attr_reader :size, :access, :modify, :change
def initialize(string)
string.split(/\n/).each do |line|
if line =~ /^Size:\s*(\d+)/
@size = .to_i
elsif line =~ /^Access:\s*(.*)/
@access = .strip # Or Date.parse(.strip)
# ...
end
end
end
end
# ...
stat_info = FileStatInfo.new(file.check_file_upload)
stat_info.size # => 11594768384
stat_info.access # => "2015-01-06 11:32:17.000000000 -0700"
一旦使用 check_file_upload()
提取了字符串 str
,就可以构建散列来保存感兴趣的值。
代码
def extract(str, keepers)
r = /\w+:\s(?:\S+\s)*\S+/
str.lines.each_with_object({}) do |line, h|
line.scan(r).each do |s|
k,_,v = s.partition(/:\s+/)
v = v.to_i if v =~ /^\d+$/
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
end
end
end
例子
我理解以下是 check_file_upload()
生成的典型字符串:
str =
"Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file
Device: 802h/2050d Inode: 57442314 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user)
Access: 2015-01-06 11:32:17.000000000 -0700
Modify: 2015-01-06 11:38:59.000000000 -0700
Change: 2015-01-06 11:38:59.000000000 -0700"
数组keepers
标识要从此字符串中提取的数据:
keepers = %w{ Size Access Modify Change }
#=> ["Size", "Access", "Modify", "Change"]
extract(str, keepers)
#=> {"Size"=>[11594768384],
# "Access"=>["(0644/-rw-r--r--)", "2015-01-06 11:32:17.000000000 -0700"],
# "Modify"=>["2015-01-06 11:38:59.000000000 -0700"],
# "Change"=>["2015-01-06 11:38:59.000000000 -0700"]}
说明
我们的正则表达式是:
r = /\w+:\s(?:\S+\s)*\S+/
对于上面的例子:
str.lines
#=> ["Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file \n",
# "Device: 802h/2050d Inode: 57442314 Links: 1\n",
# "Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user) \n",
# "Access: 2015-01-06 11:32:17.000000000 -0700 \n",
# "Modify: 2015-01-06 11:38:59.000000000 -0700 \n",
# "Change: 2015-01-06 11:38:59.000000000 -0700"]
Enumerable#each_with_object 创建一个由块变量 h
表示的初始为空的散列,并将数组 str.lines
的每个元素传递到块中,并将其分配给块变量 line
。最初,
line = "Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file \n"
h = {}
在我们计算的块内:
a = line.scan(r)
#=> ["Size: 11594768384", "Blocks: 22668184", "Block: 4096"]
请注意 "IO Block: 4096 regular file"
提取不正确,但这无关紧要,因为不需要该字段。
a.each do |s|
k,_,v = s.partition(/:\s+/)
v = v.to_i if v =~ /^\d+$/
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
end
h #=> {"Size"=>[11594768384]}
在这里,第一个字符串 each
传入它的块是
s = "Size: 11594768384"
所以
k,_,v = s.partition(/:\s+/)
#=> ["Size", ": ", "11594768384"]
v =~ /^\d+$/ #=> 0
v = v.to_i if v =~ /^\d+$/
#=> 11594768384
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
#=> {}.update({ "Size"=>[11594768384] }) if
# ["Size", "Access", "Modify", "Change"].include?("Size")
#=> true
h #=> { "Size=>[11594768384] }
我们使用 Hash#update (a.k.a. merge!
) 的形式,它使用一个块来解析包含在两个被合并的哈希值中的键的值。
由于 keepers
既不包含 "Blocks"
也不包含 "Block"
,这些键的哈希值不会合并到 h
.
对于 str.lines
的下一个元素:
line = "Device: 802h/2050d Inode: 57442314 Links: 1\n"
在 keepers
中不包含任何键,因此不会向散列 h
添加任何内容。下面一行:
line = "Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user) \n"
将 { "Access"=>["(0644/-rw-r--r--)"] }
添加到散列中,因此我们有:
h = { "Size"=>[11594768384], "Access"=>["(0644/-rw-r--r--)"] }
接下来,
line = "Access: 2015-01-06 11:32:17.000000000 -0700 \n"
所以
a = line.scan(r)
#=> ["Access: 2015-01-06 11:32:17.000000000 -0700"]
从该数组传入其块的唯一元素 each
是:
s = "Access: 2015-01-06 11:32:17.000000000 -0700"
k,_,v = s.partition(/:\s+/)
#=> ["Access", ": ", "2015-01-06 11:32:17.000000000 -0700"]
v =~ /^\d+$/
#=> nil
v = v.to_i if v =~ /^\d+$/ # no conversion to integer
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
#=> {"Size"=>[11594768384],
# "Access"=>["(0644/-rw-r--r--)", "2015-01-06 11:32:17.000000000 -0700"]}
由于h
已经包含键k => Access"
,update
的块在执行合并后被调用以确定"Access"
的值。三个块变量是:
k = "Access" # not used, so replaced with an underscore
ov = ["(0644/-rw-r--r--)" # "old" value
nv = ["2015-01-06 11:32:17.000000000 -0700"] # "new" value
"Access"
的值就是数组ov
和nv
之和,如上图
str.lines
的其余元素以类似方式处理。
我是 Ruby 编程的新手,现在我正在使用 Rbox/Rye 编写一些测试自动化 ruby 脚本。
作为测试的一部分,客户将一个 +10GB 的文件上传到 ftp 主机。
该脚本每分钟检查文件是否仍在上传、上传完成或上传失败。
对于测试,我使用 Rbox/Rye 执行 shell 命令来查找文件统计信息:
def check_file_upload()
rbox = Rye::Box.new("#{@host}")
rbox.disable_safe_mode
result = rbox.execute "stat #{@file}"
end
stats = file.check_file_upload()
puts stats
"puts" 正确打印文件统计信息。格式与我在 linux 主机上执行 "stat file" 命令相同。
现在,有没有一种方法可以让我真正解析
返回的值
check_file_upload()
方法。我可以使用 Ruby 的内置正则表达式,或者 grep/awk,从返回的文件统计信息中选择特定信息吗?
以下是文件统计:
File: `/home/user/file_name.dmp'
Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file
Device: 802h/2050d Inode: 57442314 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user)
Access: 2015-01-06 11:32:17.000000000 -0700
Modify: 2015-01-06 11:38:59.000000000 -0700
Change: 2015-01-06 11:38:59.000000000 -0700
更具体地说,我想选择文件大小、访问、修改和更改值
是的,这对于正则表达式来说是一项简单的任务。这是一个 class,它从 "stat" 字符串中解析有趣的位并将它们公开为属性:
class FileStatInfo
attr_reader :size, :access, :modify, :change
def initialize(string)
string.split(/\n/).each do |line|
if line =~ /^Size:\s*(\d+)/
@size = .to_i
elsif line =~ /^Access:\s*(.*)/
@access = .strip # Or Date.parse(.strip)
# ...
end
end
end
end
# ...
stat_info = FileStatInfo.new(file.check_file_upload)
stat_info.size # => 11594768384
stat_info.access # => "2015-01-06 11:32:17.000000000 -0700"
一旦使用 check_file_upload()
提取了字符串 str
,就可以构建散列来保存感兴趣的值。
代码
def extract(str, keepers)
r = /\w+:\s(?:\S+\s)*\S+/
str.lines.each_with_object({}) do |line, h|
line.scan(r).each do |s|
k,_,v = s.partition(/:\s+/)
v = v.to_i if v =~ /^\d+$/
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
end
end
end
例子
我理解以下是 check_file_upload()
生成的典型字符串:
str =
"Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file
Device: 802h/2050d Inode: 57442314 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user)
Access: 2015-01-06 11:32:17.000000000 -0700
Modify: 2015-01-06 11:38:59.000000000 -0700
Change: 2015-01-06 11:38:59.000000000 -0700"
数组keepers
标识要从此字符串中提取的数据:
keepers = %w{ Size Access Modify Change }
#=> ["Size", "Access", "Modify", "Change"]
extract(str, keepers)
#=> {"Size"=>[11594768384],
# "Access"=>["(0644/-rw-r--r--)", "2015-01-06 11:32:17.000000000 -0700"],
# "Modify"=>["2015-01-06 11:38:59.000000000 -0700"],
# "Change"=>["2015-01-06 11:38:59.000000000 -0700"]}
说明
我们的正则表达式是:
r = /\w+:\s(?:\S+\s)*\S+/
对于上面的例子:
str.lines
#=> ["Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file \n",
# "Device: 802h/2050d Inode: 57442314 Links: 1\n",
# "Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user) \n",
# "Access: 2015-01-06 11:32:17.000000000 -0700 \n",
# "Modify: 2015-01-06 11:38:59.000000000 -0700 \n",
# "Change: 2015-01-06 11:38:59.000000000 -0700"]
Enumerable#each_with_object 创建一个由块变量 h
表示的初始为空的散列,并将数组 str.lines
的每个元素传递到块中,并将其分配给块变量 line
。最初,
line = "Size: 11594768384 Blocks: 22668184 IO Block: 4096 regular file \n"
h = {}
在我们计算的块内:
a = line.scan(r)
#=> ["Size: 11594768384", "Blocks: 22668184", "Block: 4096"]
请注意 "IO Block: 4096 regular file"
提取不正确,但这无关紧要,因为不需要该字段。
a.each do |s|
k,_,v = s.partition(/:\s+/)
v = v.to_i if v =~ /^\d+$/
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
end
h #=> {"Size"=>[11594768384]}
在这里,第一个字符串 each
传入它的块是
s = "Size: 11594768384"
所以
k,_,v = s.partition(/:\s+/)
#=> ["Size", ": ", "11594768384"]
v =~ /^\d+$/ #=> 0
v = v.to_i if v =~ /^\d+$/
#=> 11594768384
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
#=> {}.update({ "Size"=>[11594768384] }) if
# ["Size", "Access", "Modify", "Change"].include?("Size")
#=> true
h #=> { "Size=>[11594768384] }
我们使用 Hash#update (a.k.a. merge!
) 的形式,它使用一个块来解析包含在两个被合并的哈希值中的键的值。
由于 keepers
既不包含 "Blocks"
也不包含 "Block"
,这些键的哈希值不会合并到 h
.
对于 str.lines
的下一个元素:
line = "Device: 802h/2050d Inode: 57442314 Links: 1\n"
在 keepers
中不包含任何键,因此不会向散列 h
添加任何内容。下面一行:
line = "Access: (0644/-rw-r--r--) Uid: ( 1504/user) Gid: ( 1504/user) \n"
将 { "Access"=>["(0644/-rw-r--r--)"] }
添加到散列中,因此我们有:
h = { "Size"=>[11594768384], "Access"=>["(0644/-rw-r--r--)"] }
接下来,
line = "Access: 2015-01-06 11:32:17.000000000 -0700 \n"
所以
a = line.scan(r)
#=> ["Access: 2015-01-06 11:32:17.000000000 -0700"]
从该数组传入其块的唯一元素 each
是:
s = "Access: 2015-01-06 11:32:17.000000000 -0700"
k,_,v = s.partition(/:\s+/)
#=> ["Access", ": ", "2015-01-06 11:32:17.000000000 -0700"]
v =~ /^\d+$/
#=> nil
v = v.to_i if v =~ /^\d+$/ # no conversion to integer
h.update({ k=>[v] }) { |_,ov,nv| ov+nv } if keepers.include?(k)
#=> {"Size"=>[11594768384],
# "Access"=>["(0644/-rw-r--r--)", "2015-01-06 11:32:17.000000000 -0700"]}
由于h
已经包含键k => Access"
,update
的块在执行合并后被调用以确定"Access"
的值。三个块变量是:
k = "Access" # not used, so replaced with an underscore
ov = ["(0644/-rw-r--r--)" # "old" value
nv = ["2015-01-06 11:32:17.000000000 -0700"] # "new" value
"Access"
的值就是数组ov
和nv
之和,如上图
str.lines
的其余元素以类似方式处理。