persistent-shell-history 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/README +34 -0
- data/Rakefile +4 -0
- data/TODOs +17 -0
- data/bin/bash_history.rb +89 -0
- data/lib/persistent-shell-history.rb +9 -0
- data/lib/persistent-shell-history/abstract-history-store.rb +17 -0
- data/lib/persistent-shell-history/binary-history-store.rb +129 -0
- data/lib/persistent-shell-history/command.rb +21 -0
- data/lib/persistent-shell-history/history.rb +43 -0
- data/lib/persistent-shell-history/old-history-store.rb +141 -0
- data/lib/persistent-shell-history/version.rb +7 -0
- data/persistent-shell-history.gemspec +29 -0
- metadata +113 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
This is a quick job, to have a local database, to collect _all_ commands
|
2
|
+
from ~/.bash_history
|
3
|
+
|
4
|
+
To couple with this, I have set a crontab job:
|
5
|
+
*/30 * * * * /home/vbatts/bin/bash_history.rb
|
6
|
+
|
7
|
+
|
8
|
+
and set in ~/.bashrc:
|
9
|
+
unset HISTFILESIZE
|
10
|
+
export HISTSIZE=10000
|
11
|
+
export HISTTIMEFORMAT="%F %T "
|
12
|
+
export HISTCONTROL="ignoreboth"
|
13
|
+
|
14
|
+
|
15
|
+
== USAGE
|
16
|
+
See the --help also,
|
17
|
+
Usage: bash_history [options]
|
18
|
+
--inspect inspect the data
|
19
|
+
-h, --history FILE use bash_history FILE instead of the default (~/.bash_history)
|
20
|
+
-d, --db FILE use database FILE instead of the default (~/.bash_history.db)
|
21
|
+
-l, --list list history
|
22
|
+
--fix fix times
|
23
|
+
--format FORMAT specify a different strftime format. (default is "%F %T")
|
24
|
+
-f, --find PAT find a command with pattern PAT
|
25
|
+
|
26
|
+
|
27
|
+
== LICENSE
|
28
|
+
Copyright (c) 2012 Vincent Batts, Raleigh, NC, USA
|
29
|
+
|
30
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
31
|
+
|
32
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
33
|
+
|
34
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/TODOs
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
* add a ~/.persistent-shell-history.yaml
|
2
|
+
* move the db from gdbm to sqlite
|
3
|
+
- tables
|
4
|
+
** hostname
|
5
|
+
-- id
|
6
|
+
-- name
|
7
|
+
** command
|
8
|
+
-- id
|
9
|
+
-- value
|
10
|
+
** record
|
11
|
+
-- id
|
12
|
+
-- timestamp
|
13
|
+
-- hostname_id
|
14
|
+
-- command_id
|
15
|
+
* migration from current Marshal version need only to check the value, for if
|
16
|
+
it starts with "\x04\b{"
|
17
|
+
|
data/bin/bash_history.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'persistent-shell-history/binary-history-store'
|
4
|
+
require 'persistent-shell-history/old-history-store'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
bh_options = {:archive_file => File.expand_path("~/.bash_history.db")}
|
9
|
+
opts = OptionParser.new do |opts|
|
10
|
+
opts.on('--inspect','inspect the data') do |o|
|
11
|
+
options[:inspect] = o
|
12
|
+
end
|
13
|
+
opts.on('-h','--history FILE','use bash_history FILE instead of the default (~/.bash_history)') do |o|
|
14
|
+
bh_options[:file] = o
|
15
|
+
end
|
16
|
+
opts.on('-d','--db FILE','use database FILE instead of the default (~/.bash_history.db)') do |o|
|
17
|
+
bh_options[:archive_file] = o
|
18
|
+
end
|
19
|
+
opts.on('--migrate','check-for and migrate, only') do |o|
|
20
|
+
options[:migrate] = o
|
21
|
+
end
|
22
|
+
opts.on('-l','--list','list history') do |o|
|
23
|
+
options[:list] = o
|
24
|
+
end
|
25
|
+
opts.on('--format FORMAT','specify a different strftime format. (default is "%F %T")') do |o|
|
26
|
+
bh_options[:time_format] = o
|
27
|
+
end
|
28
|
+
opts.on('-f','--find PAT','find a command with pattern PAT') do |o|
|
29
|
+
options[:find] = o
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
opts.parse!(ARGV)
|
35
|
+
rescue => ex
|
36
|
+
puts ex
|
37
|
+
puts opts
|
38
|
+
end
|
39
|
+
|
40
|
+
def migrate(old_bashhistorystore)
|
41
|
+
require 'fileutils'
|
42
|
+
# migrate
|
43
|
+
temp_db = GDBM.new("#{old_bashhistorystore.archive}.#{$$}")
|
44
|
+
old_bashhistorystore.keys.each {|key|
|
45
|
+
h = old_bashhistorystore[key]
|
46
|
+
h[:time] = h[:time].map {|t| t.to_i }
|
47
|
+
temp_db[key] = Marshal.dump(h)
|
48
|
+
}
|
49
|
+
old_bashhistorystore.db.close()
|
50
|
+
temp_db.close()
|
51
|
+
ts = "#{Time.now.year}#{Time.now.month}#{Time.now.day}#{Time.now.hour}#{Time.now.min}"
|
52
|
+
old_filename = "#{old_bashhistorystore.archive}.old.#{ts}"
|
53
|
+
FileUtils.mv(old_bashhistorystore.archive, old_filename)
|
54
|
+
puts "archived [#{old_bashhistorystore.archive}] to [#{old_filename}] ..."
|
55
|
+
FileUtils.mv("#{old_bashhistorystore.archive}.#{$$}", old_bashhistorystore.archive)
|
56
|
+
end
|
57
|
+
|
58
|
+
# First check the database, for whether it is the old format,
|
59
|
+
# if so, convert it, and reopen it.
|
60
|
+
bh = Persistent::Shell::OldHistoryStore.new(bh_options)
|
61
|
+
migrate(bh) if bh.is_oldformat?
|
62
|
+
bh.db.close unless bh.db.closed?
|
63
|
+
|
64
|
+
exit(0) if options[:migrate]
|
65
|
+
|
66
|
+
# re-open the history storage
|
67
|
+
bh = Persistent::Shell::BinaryHistoryStore.new(bh_options)
|
68
|
+
|
69
|
+
# load the new bash_history into the database
|
70
|
+
unless (options[:inspect] or options[:find] or options[:list])
|
71
|
+
bh.load()
|
72
|
+
bh.db.reorganize()
|
73
|
+
end
|
74
|
+
|
75
|
+
if options[:inspect]
|
76
|
+
p bh
|
77
|
+
#p "storing #{bh.keys.count} commands"
|
78
|
+
end
|
79
|
+
if options[:find]
|
80
|
+
bh.find(options[:find]).sort_by {|x| x[:time] }.each do |val|
|
81
|
+
puts bh.fmt(val)
|
82
|
+
end
|
83
|
+
elsif options[:list]
|
84
|
+
bh.values_by_time.each do |val|
|
85
|
+
puts bh.fmt(val)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# vim: set sts=2 sw=2 et ai:
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module Persistent
|
3
|
+
module Shell
|
4
|
+
|
5
|
+
class AbstractHistoryStore
|
6
|
+
SCHEMA_VERSION = "1"
|
7
|
+
|
8
|
+
def commands; end
|
9
|
+
def db; end
|
10
|
+
def shema_match?; db.has_key? "schema_version" and db["schema_version"] == SCHEMA_VERSION; end
|
11
|
+
def shema_version; db["schema_version"] if db.has_key? "schema_version" ; end
|
12
|
+
end # class AbstractHistoryStore
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# vim: set sts=2 sw=2 et ai:
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'gdbm'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'persistent-shell-history/abstract-history-store'
|
6
|
+
require 'persistent-shell-history/history'
|
7
|
+
require 'persistent-shell-history/command'
|
8
|
+
|
9
|
+
if RUBY_VERSION >= "1.9" # assuming you're running Ruby ~1.9
|
10
|
+
Encoding.default_external = Encoding::UTF_8
|
11
|
+
Encoding.default_internal = Encoding::UTF_8
|
12
|
+
end
|
13
|
+
|
14
|
+
module Persistent
|
15
|
+
module Shell
|
16
|
+
class BinaryHistoryStore < AbstractHistoryStore
|
17
|
+
OPTIONS = {
|
18
|
+
:file => File.expand_path("~/.bash_history"),
|
19
|
+
:archive_file => File.expand_path("~/.bash_history.db"),
|
20
|
+
:time_format => "%F %T",
|
21
|
+
}
|
22
|
+
|
23
|
+
def initialize(opts = {})
|
24
|
+
@options = OPTIONS.merge(opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
def archive; @options[:archive_file]; end
|
28
|
+
def archive=(arg); @options[:archive_file] = arg; end
|
29
|
+
|
30
|
+
def hist_file; @options[:file]; end
|
31
|
+
def hist_file=(arg); @options[:file] = arg; end
|
32
|
+
|
33
|
+
def time_format; @options[:time_format]; end
|
34
|
+
def time_format=(tf); @options[:time_format] = tf; end
|
35
|
+
def db
|
36
|
+
@db ||= GDBM.new(@options[:archive_file])
|
37
|
+
end
|
38
|
+
def [](key); _ml(db[key]); end
|
39
|
+
def keys; db.keys; end
|
40
|
+
def values; db.map {|k,v| _ml(v) }; end
|
41
|
+
def values_by_time
|
42
|
+
return db.map {|k,v|
|
43
|
+
data = _ml(v)
|
44
|
+
data[:time].map {|t|
|
45
|
+
data.merge(:time => t)
|
46
|
+
}
|
47
|
+
}.flatten.sort_by {|x|
|
48
|
+
x[:time]
|
49
|
+
}
|
50
|
+
end
|
51
|
+
def commands; values.map {|v| v[:cmd] }; end
|
52
|
+
def _md(data); Marshal.dump(data); end
|
53
|
+
def _ml(data); Marshal.load(data); end
|
54
|
+
def _md5(data); Digest::MD5.hexdigest(data); end
|
55
|
+
|
56
|
+
# display a formatted time commend
|
57
|
+
def fmt(cmd); " %s %s" % [Time.at(cmd[:time]).strftime(@options[:time_format]), cmd[:cmd]]; end
|
58
|
+
|
59
|
+
def find(pat)
|
60
|
+
return values.select {|v|
|
61
|
+
v if v[:cmd] =~ /#{pat}/
|
62
|
+
}.map {|v|
|
63
|
+
v[:time].map {|t|
|
64
|
+
v.merge(:time => t)
|
65
|
+
}
|
66
|
+
}.flatten
|
67
|
+
end
|
68
|
+
|
69
|
+
def load(filename = @options[:file])
|
70
|
+
open(filename) do |f|
|
71
|
+
f.each_line do |line|
|
72
|
+
if line =~ /^#(.*)$/
|
73
|
+
l = f.readline.chomp
|
74
|
+
key = _md5(l)
|
75
|
+
if db.has_key?(key)
|
76
|
+
times = _ml(db[key])[:time]
|
77
|
+
if times.kind_of? Array
|
78
|
+
times.push($1.to_i)
|
79
|
+
else
|
80
|
+
times = [times]
|
81
|
+
end
|
82
|
+
db[key] = _md({:cmd => l, :time => times.uniq })
|
83
|
+
else
|
84
|
+
db[key] = _md({:cmd => l, :time => [$1.to_i] })
|
85
|
+
end
|
86
|
+
else
|
87
|
+
key = _md5(line.chomp)
|
88
|
+
if db.has_key?(key)
|
89
|
+
times = _ml(db[key])[:time]
|
90
|
+
if times.kind_of? Array
|
91
|
+
times.push($1.to_i)
|
92
|
+
else
|
93
|
+
times = [times]
|
94
|
+
end
|
95
|
+
db[key] = _md({:cmd => l, :time => times.uniq })
|
96
|
+
else
|
97
|
+
db[key] = _md({:cmd => line.chomp, :time => [0] })
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# returns a Persistent::Shell::History object from the current GDBM database.
|
105
|
+
# intended for marshalling to other history-stores
|
106
|
+
def to_history
|
107
|
+
history = History.new
|
108
|
+
values.each do |value|
|
109
|
+
value[:time].each do |t|
|
110
|
+
history << Command.new(value[:cmd], t.to_i)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
return history
|
114
|
+
end
|
115
|
+
|
116
|
+
# create an output that looks like a regular ~/.bash_history file
|
117
|
+
def render(file)
|
118
|
+
File.open(file,'w+') do |f|
|
119
|
+
values.each do |v|
|
120
|
+
f.write("#" + v[:time].to_i.to_s + "\n") if v[:time] and not (v[:time].to_i == 0)
|
121
|
+
f.write(v[:cmd] + "\n")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end # class BinaryHistoryStore
|
127
|
+
end # Shell
|
128
|
+
end # Persistent
|
129
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Persistent
|
6
|
+
module Shell
|
7
|
+
class Command < Struct.new(:cmd, :time)
|
8
|
+
def md5
|
9
|
+
Digest::MD5.hexdigest(cmd)
|
10
|
+
end
|
11
|
+
def to_h
|
12
|
+
{ :cmd => cmd, :time => time, }
|
13
|
+
end
|
14
|
+
def to_json(*a)
|
15
|
+
to_h.to_json(*a)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# vim: set sts=2 sw=2 et ai:
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
require 'persistent-shell-history/command'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Persistent
|
6
|
+
module Shell
|
7
|
+
# Abstract storage for command history
|
8
|
+
class History
|
9
|
+
def initialize()
|
10
|
+
@cmds = Array.new
|
11
|
+
end
|
12
|
+
def commands; @cmds; end
|
13
|
+
def commands=(cmds); @cmds = cmds; end
|
14
|
+
def <<(arg); @cmds << arg; end
|
15
|
+
def to_a; commands.map {|c| c.to_h }; end
|
16
|
+
def to_json(*a); commands.to_json(*a); end
|
17
|
+
end
|
18
|
+
class BashHistory < History
|
19
|
+
def initialize(filename = '~/.bash_history')
|
20
|
+
@filename = File.expand_path(filename)
|
21
|
+
end
|
22
|
+
def commands; (@cmds.nil? or @cmds.empty?) ? (@cmds = parse) : @cmds; end
|
23
|
+
def file; @filename; end
|
24
|
+
def file=(filename); @filename = File.expand_path(filename); end
|
25
|
+
def parse(filename = @filename)
|
26
|
+
cmds = Array.new
|
27
|
+
open(filename) do |f|
|
28
|
+
f.each_line do |line|
|
29
|
+
if line =~ /^#(.*)$/
|
30
|
+
l = f.readline.chomp
|
31
|
+
cmds << Command.new(l, $1)
|
32
|
+
else
|
33
|
+
cmds << Command.new(line, "0")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return cmds
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# vim: set sts=2 sw=2 et ai:
|
@@ -0,0 +1,141 @@
|
|
1
|
+
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'gdbm'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require 'persistent-shell-history/abstract-history-store'
|
7
|
+
require 'persistent-shell-history/history'
|
8
|
+
require 'persistent-shell-history/command'
|
9
|
+
|
10
|
+
module Persistent
|
11
|
+
module Shell
|
12
|
+
class OldHistoryStore < AbstractHistoryStore
|
13
|
+
OPTIONS = {
|
14
|
+
:file => File.expand_path("~/.bash_history"),
|
15
|
+
:archive_file => File.expand_path("~/.bash_history.db"),
|
16
|
+
:time_format => "%F %T",
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize(opts = {})
|
20
|
+
@options = OPTIONS.merge(opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def archive; @options[:archive_file]; end
|
24
|
+
def archive=(arg); @options[:archive_file] = arg; end
|
25
|
+
|
26
|
+
def hist_file; @options[:file]; end
|
27
|
+
def hist_file=(arg); @options[:file] = arg; end
|
28
|
+
|
29
|
+
# check the archive, whether it's the old format.
|
30
|
+
# If so, it needs to be converted to the BinaryHistoryStore
|
31
|
+
def is_oldformat?
|
32
|
+
db.keys[0..5].each {|key|
|
33
|
+
begin
|
34
|
+
YAML.load(db[key])
|
35
|
+
rescue Psych::SyntaxError => ex
|
36
|
+
return false
|
37
|
+
rescue => ex
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
}
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
|
44
|
+
def time_format; @options[:time_format]; end
|
45
|
+
def time_format=(tf); @options[:time_format] = tf; end
|
46
|
+
def db
|
47
|
+
@db ||= GDBM.new(@options[:archive_file])
|
48
|
+
end
|
49
|
+
def [](key); _yl(db[key]); end
|
50
|
+
def keys; db.keys; end
|
51
|
+
def keys_to_i; keys.map {|i| i.to_i }; end
|
52
|
+
def values; db.map {|k,v| _yl(v) }; end
|
53
|
+
def values_by_time
|
54
|
+
return db.map {|k,v|
|
55
|
+
data = _yl(v)
|
56
|
+
data[:time].map {|t|
|
57
|
+
data.merge(:time => t)
|
58
|
+
}
|
59
|
+
}.flatten.sort_by {|x|
|
60
|
+
x[:time]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
def commands; values.map {|v| v[:cmd] }; end
|
64
|
+
def _yd(data); YAML.dump(data); end
|
65
|
+
def _yl(data); YAML.load(data); end
|
66
|
+
def _md5(data); Digest::MD5.hexdigest(data); end
|
67
|
+
|
68
|
+
# display a formatted time commend
|
69
|
+
def fmt(cmd); " %s %s" % [cmd[:time].strftime(@options[:time_format]), cmd[:cmd]]; end
|
70
|
+
|
71
|
+
def find(pat)
|
72
|
+
return values.select {|v|
|
73
|
+
v if v[:cmd] =~ /#{pat}/
|
74
|
+
}.map {|v|
|
75
|
+
v[:time].map {|t|
|
76
|
+
v.merge(:time => t)
|
77
|
+
}
|
78
|
+
}.flatten
|
79
|
+
end
|
80
|
+
|
81
|
+
def load(filename = @options[:file])
|
82
|
+
open(filename) do |f|
|
83
|
+
f.each_line do |line|
|
84
|
+
if line =~ /^#(.*)$/
|
85
|
+
l = f.readline.chomp
|
86
|
+
key = _md5(l)
|
87
|
+
if db.has_key?(key)
|
88
|
+
times = _yl(db[key])[:time]
|
89
|
+
if times.kind_of? Array
|
90
|
+
times.push(Time.at($1.to_i))
|
91
|
+
else
|
92
|
+
times = [times]
|
93
|
+
end
|
94
|
+
db[key] = _yd({:cmd => l, :time => times.uniq })
|
95
|
+
else
|
96
|
+
db[key] = _yd({:cmd => l, :time => [Time.at($1.to_i)] })
|
97
|
+
end
|
98
|
+
else
|
99
|
+
key = _md5(line.chomp)
|
100
|
+
if db.has_key?(key)
|
101
|
+
times = _yl(db[key])[:time]
|
102
|
+
if times.kind_of? Array
|
103
|
+
times.push(Time.at($1.to_i))
|
104
|
+
else
|
105
|
+
times = [times]
|
106
|
+
end
|
107
|
+
db[key] = _yd({:cmd => l, :time => times.uniq })
|
108
|
+
else
|
109
|
+
db[key] = _yd({:cmd => line.chomp, :time => [Time.at(0)] })
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# returns a Persistent::Shell::History object from the current GDBM database.
|
117
|
+
# intended for marshalling to other history-stores
|
118
|
+
def to_history
|
119
|
+
history = History.new
|
120
|
+
values.each do |value|
|
121
|
+
value[:time].each do |t|
|
122
|
+
history << Command.new(value[:cmd], t.to_i)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
return history
|
126
|
+
end
|
127
|
+
|
128
|
+
# create an output that looks like a regular ~/.bash_history file
|
129
|
+
def render(file)
|
130
|
+
File.open(file,'w+') do |f|
|
131
|
+
values.each do |v|
|
132
|
+
f.write("#" + v[:time].to_i.to_s + "\n") if v[:time] and not (v[:time].to_i == 0)
|
133
|
+
f.write(v[:cmd] + "\n")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end # class DataStore
|
138
|
+
end # module Shell
|
139
|
+
end # module Persistent
|
140
|
+
|
141
|
+
# vim: set sts=2 sw=2 et ai:
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "persistent-shell-history/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "persistent-shell-history"
|
7
|
+
s.version = Persistent::Shell::VERSION
|
8
|
+
s.authors = ["Vincent Batts"]
|
9
|
+
s.email = ["vbatts@hashbangbash.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{bash shell history collection}
|
12
|
+
s.description = %q{
|
13
|
+
This is a quick job, to have a local database, to collect _all_ commands
|
14
|
+
from ~/.bash_history
|
15
|
+
See README for other implementation helpers.
|
16
|
+
}
|
17
|
+
|
18
|
+
s.rubyforge_project = "persistent-shell-history"
|
19
|
+
s.add_dependency("json")
|
20
|
+
s.add_dependency("gdbm")
|
21
|
+
s.add_dependency("ffi")
|
22
|
+
|
23
|
+
s.add_dependency("activerecord")
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.require_paths = ["lib"]
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: persistent-shell-history
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Vincent Batts
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-12-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: &11562800 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *11562800
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: gdbm
|
27
|
+
requirement: &11562380 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *11562380
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: ffi
|
38
|
+
requirement: &11561960 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *11561960
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: activerecord
|
49
|
+
requirement: &11561540 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *11561540
|
58
|
+
description: ! '
|
59
|
+
|
60
|
+
This is a quick job, to have a local database, to collect _all_ commands
|
61
|
+
|
62
|
+
from ~/.bash_history
|
63
|
+
|
64
|
+
See README for other implementation helpers.
|
65
|
+
|
66
|
+
'
|
67
|
+
email:
|
68
|
+
- vbatts@hashbangbash.com
|
69
|
+
executables:
|
70
|
+
- bash_history.rb
|
71
|
+
extensions: []
|
72
|
+
extra_rdoc_files: []
|
73
|
+
files:
|
74
|
+
- .gitignore
|
75
|
+
- Gemfile
|
76
|
+
- README
|
77
|
+
- Rakefile
|
78
|
+
- TODOs
|
79
|
+
- bin/bash_history.rb
|
80
|
+
- lib/persistent-shell-history.rb
|
81
|
+
- lib/persistent-shell-history/abstract-history-store.rb
|
82
|
+
- lib/persistent-shell-history/binary-history-store.rb
|
83
|
+
- lib/persistent-shell-history/command.rb
|
84
|
+
- lib/persistent-shell-history/history.rb
|
85
|
+
- lib/persistent-shell-history/old-history-store.rb
|
86
|
+
- lib/persistent-shell-history/version.rb
|
87
|
+
- persistent-shell-history.gemspec
|
88
|
+
homepage: ''
|
89
|
+
licenses: []
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project: persistent-shell-history
|
108
|
+
rubygems_version: 1.8.17
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: bash shell history collection
|
112
|
+
test_files: []
|
113
|
+
has_rdoc:
|