unix_utils 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +7 -0
- data/History.txt +9 -0
- data/README.markdown +77 -0
- data/Rakefile +12 -0
- data/lib/unix_utils.rb +276 -0
- data/lib/unix_utils/version.rb +3 -0
- data/test/helper.rb +73 -0
- data/test/target/directory-really-a-t_a_r-shh +0 -0
- data/test/target/directory-really-a-z_i_p-shh +0 -0
- data/test/target/directory.tar +0 -0
- data/test/target/directory.zip +0 -0
- data/test/target/directory/hello_world.txt +1 -0
- data/test/target/directory/hello_world.xml +1 -0
- data/test/target/file-really-a-b_z_2-shh +0 -0
- data/test/target/file-really-a-g_z-shh +0 -0
- data/test/target/file.bz2 +0 -0
- data/test/target/file.gz +0 -0
- data/test/target/iso-8859-1.txt +1 -0
- data/test/target/utf8.txt +1 -0
- data/test/test_unix_utils.rb +383 -0
- data/unix_utils.gemspec +18 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/History.txt
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# unix_utils
|
2
|
+
|
3
|
+
Like FileUtils, but provides zip, unzip, bzip2, bunzip2, tar, untar, sed, du, md5sum, shasum, cut, head, tail, wc, unix2dos, dos2unix, iconv, curl, perl, etc.
|
4
|
+
|
5
|
+
Works in MRI 1.8.7+, MRI 1.9.2+, and JRuby 1.6.7+
|
6
|
+
|
7
|
+
## What to expect
|
8
|
+
|
9
|
+
For commands like zip, untar, sed, head, cut, dos2unix, etc.:
|
10
|
+
|
11
|
+
1. Just returns a path to the output, randomly named, located in the system tmp dir (`UnixUtils.unzip('kittens.zip)` → `'/tmp/unix_utils-129392301-kittens'`)
|
12
|
+
2. Never touches the input
|
13
|
+
3. Sticks a useful file extension on the output, if applicable (`UnixUtils.tar('puppies/')` → `'/tmp/unix_utils-99293192-puppies.tar'`)
|
14
|
+
|
15
|
+
For commands like du, md5sum, shasum, etc.:
|
16
|
+
|
17
|
+
1. Just returns the good stuff (the checksum, for example, not the filename that is listed after it in the standard command output)
|
18
|
+
2. Never touches the input
|
19
|
+
|
20
|
+
## Philosophy
|
21
|
+
|
22
|
+
Use a subprocess to perform a big task and then get out of memory.
|
23
|
+
|
24
|
+
## But I can just spawn these myself
|
25
|
+
|
26
|
+
This lib was created to ease the pain of remembering command options for Gentoo, deciding which spawning method to use, possibly handling pipes...
|
27
|
+
|
28
|
+
require 'tmpdir'
|
29
|
+
destdir = File.join(Dir.tmpdir, "kittens_#{Kernel.rand(1e11)}")
|
30
|
+
require 'open3'
|
31
|
+
Open3.popen3('unzip', '-q', '-n', 'kittens.zip, '-d', destdir) do |stdin, stdout, stderr|
|
32
|
+
stdin.close
|
33
|
+
@error_message = stderr.read
|
34
|
+
end
|
35
|
+
|
36
|
+
is replaced safely with
|
37
|
+
|
38
|
+
destdir = UnixUtils.unzip 'kittens.zip'
|
39
|
+
|
40
|
+
## But I can just use `Digest::SHA256`
|
41
|
+
|
42
|
+
This will load an entire file into memory before it can be processed...
|
43
|
+
|
44
|
+
require 'digest'
|
45
|
+
str = Digest::SHA256.hexdigest File.read('kittens.zip')
|
46
|
+
|
47
|
+
... so you're really replacing this ...
|
48
|
+
|
49
|
+
sha256 = Digest::SHA256.new
|
50
|
+
File.open('kittens.zip', 'r') do |f|
|
51
|
+
while chunk = f.read(4_194_304)
|
52
|
+
sha256 << chunk
|
53
|
+
end
|
54
|
+
end
|
55
|
+
str = sha256.hexdigest
|
56
|
+
|
57
|
+
You get the same low memory footprint with
|
58
|
+
|
59
|
+
str = UnixUtils.shasum('kittens.zip', 256)
|
60
|
+
|
61
|
+
## Compatibility
|
62
|
+
|
63
|
+
Uses `open3` because it's in the Ruby stdlib and is consistent across MRI and JRuby.
|
64
|
+
|
65
|
+
## Where it's used
|
66
|
+
|
67
|
+
* [Brighter Planet Reference Data web service](http://data.brighterplanet.com)
|
68
|
+
* [Brighter Planet Emission Estimate web service](http://impact.brighterplanet.com) aka CM1
|
69
|
+
* [`remote_table` library](https://github.com/seamusabshere/remote_table)
|
70
|
+
|
71
|
+
## Authors
|
72
|
+
|
73
|
+
* Seamus Abshere <seamus@abshere.net>
|
74
|
+
|
75
|
+
## Copyright
|
76
|
+
|
77
|
+
Copyright (c) 2012 Brighter Planet. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:test) do |test|
|
7
|
+
test.libs << 'lib' << 'test'
|
8
|
+
test.pattern = 'test/**/test_*.rb'
|
9
|
+
test.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :test
|
data/lib/unix_utils.rb
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'uri'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
require "unix_utils/version"
|
7
|
+
|
8
|
+
module UnixUtils
|
9
|
+
|
10
|
+
def self.curl(url, form_data = nil)
|
11
|
+
outfile = tmp_path url
|
12
|
+
if url.start_with?('/') or url.start_with?('file://')
|
13
|
+
# deal with local files
|
14
|
+
::FileUtils.cp url.delete('file://'), path
|
15
|
+
return outfile
|
16
|
+
end
|
17
|
+
uri = ::URI.parse url
|
18
|
+
argv = [ 'curl', '--location', '--show-error', '--silent', '--compressed', '--header', 'Expect: ' ]
|
19
|
+
if form_data
|
20
|
+
argv += [ '--data', form_data ]
|
21
|
+
end
|
22
|
+
argv += [ uri.to_s, '--output', outfile ]
|
23
|
+
spawn argv
|
24
|
+
outfile
|
25
|
+
end
|
26
|
+
|
27
|
+
#--
|
28
|
+
# most platforms
|
29
|
+
# $ openssl dgst -sha256 .bash_profile
|
30
|
+
# SHA256(.bash_profile)= ae12206aaa35dc96273ed421f4e85ca26a1707455e3cc9f054c7f5e2e9c53df6
|
31
|
+
# ubuntu 11.04
|
32
|
+
# $ shasum -a 256 --portable .mysql_history
|
33
|
+
# 856aa27deb0b80b41031c2ddf722af28ba2a8c4999ff9cf2d45f33bc67d992ba ?.mysql_history
|
34
|
+
# fedora 7
|
35
|
+
# $ sha256sum --binary .bash_profile
|
36
|
+
# 01b1210962b3d1e5e1ccba26f93d98efbb7b315b463f9f6bdb40ab496728d886 *.bash_profile
|
37
|
+
def self.shasum(infile, algorithm)
|
38
|
+
if available?('shasum')
|
39
|
+
argv = ['shasum', '--binary', '-a', algorithm.to_s, infile]
|
40
|
+
stdout = spawn argv
|
41
|
+
stdout.strip.split(' ').first
|
42
|
+
else
|
43
|
+
argv = ['openssl', 'dgst', "-sha#{algorithm}", infile]
|
44
|
+
stdout = spawn argv
|
45
|
+
stdout.strip.split(' ').last
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#--
|
50
|
+
# os x 10.6.8; most platforms
|
51
|
+
# $ openssl dgst -md5 .bashrc
|
52
|
+
# MD5(.bashrc)= 88f464fb6d1d6fe9141135248bf7b265
|
53
|
+
# ubuntu 11.04; fedora 7; gentoo
|
54
|
+
# $ md5sum --binary .mysql_history
|
55
|
+
# 8d01e54ab8142d6786850e22d55a1b6c *.mysql_history
|
56
|
+
def self.md5sum(infile)
|
57
|
+
if available?('md5sum')
|
58
|
+
argv = ['md5sum', '--binary', infile]
|
59
|
+
stdout = spawn argv
|
60
|
+
stdout.strip.split(' ').first
|
61
|
+
else
|
62
|
+
argv = ['openssl', 'dgst', '-md5', infile]
|
63
|
+
stdout = spawn argv
|
64
|
+
stdout.strip.split(' ').last
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.du(srcdir)
|
69
|
+
argv = ['du', srcdir]
|
70
|
+
stdout = spawn argv
|
71
|
+
stdout.strip.split(/\s+/).first.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.wc(infile)
|
75
|
+
argv = ['wc', infile]
|
76
|
+
stdout = spawn argv
|
77
|
+
stdout.strip.split(/\s+/)[0..2].map { |s| s.to_i }
|
78
|
+
end
|
79
|
+
|
80
|
+
# --
|
81
|
+
|
82
|
+
def self.unzip(infile)
|
83
|
+
destdir = tmp_path infile
|
84
|
+
::FileUtils.mkdir destdir
|
85
|
+
argv = ['unzip', '-qq', '-n', infile, '-d', destdir]
|
86
|
+
spawn argv
|
87
|
+
destdir
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.untar(infile)
|
91
|
+
destdir = tmp_path infile
|
92
|
+
::FileUtils.mkdir destdir
|
93
|
+
argv = ['tar', '-xf', infile, '-C', destdir]
|
94
|
+
spawn argv
|
95
|
+
destdir
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.gunzip(infile)
|
99
|
+
outfile = tmp_path infile
|
100
|
+
argv = ['gunzip', '--stdout', infile]
|
101
|
+
spawn argv, :write_to => outfile
|
102
|
+
outfile
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.bunzip2(infile)
|
106
|
+
outfile = tmp_path infile
|
107
|
+
argv = ['bunzip2', '--stdout', infile,]
|
108
|
+
spawn argv, :write_to => outfile
|
109
|
+
outfile
|
110
|
+
end
|
111
|
+
|
112
|
+
# --
|
113
|
+
|
114
|
+
def self.bzip2(infile)
|
115
|
+
outfile = tmp_path infile, '.bz2'
|
116
|
+
argv = ['bzip2', '--keep', '--stdout', infile]
|
117
|
+
spawn argv, :write_to => outfile
|
118
|
+
outfile
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.tar(srcdir)
|
122
|
+
outfile = tmp_path srcdir, '.tar'
|
123
|
+
argv = ['tar', '-cf', outfile, '-C', srcdir, '.']
|
124
|
+
spawn argv
|
125
|
+
outfile
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.zip(srcdir)
|
129
|
+
outfile = tmp_path srcdir, '.zip'
|
130
|
+
argv = ['zip', '-rq', outfile, '.']
|
131
|
+
spawn argv, :chdir => srcdir
|
132
|
+
outfile
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.gzip(infile)
|
136
|
+
outfile = tmp_path infile, '.gz'
|
137
|
+
argv = ['gzip', '--stdout', infile]
|
138
|
+
spawn argv, :write_to => outfile
|
139
|
+
outfile
|
140
|
+
end
|
141
|
+
|
142
|
+
# --
|
143
|
+
|
144
|
+
def self.awk(infile, *expr)
|
145
|
+
outfile = tmp_path infile
|
146
|
+
bin = available?('gawk') ? 'gawk' : 'awk'
|
147
|
+
argv = [bin, expr, infile].flatten
|
148
|
+
spawn argv, :write_to => outfile
|
149
|
+
outfile
|
150
|
+
end
|
151
|
+
|
152
|
+
# Yes, this is a very limited use of perl.
|
153
|
+
def self.perl(infile, *expr)
|
154
|
+
outfile = tmp_path infile
|
155
|
+
argv = [ 'perl', expr.map { |e| ['-pe', e] } ].flatten
|
156
|
+
spawn argv, :read_from => infile, :write_to => outfile
|
157
|
+
outfile
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.unix2dos(infile)
|
161
|
+
if available?('gawk') or available?('awk')
|
162
|
+
awk infile, '{ sub(/\r/, ""); printf("%s\r\n", $0) }'
|
163
|
+
else
|
164
|
+
perl infile, 's/\r\n|\n|\r/\r\n/g'
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.dos2unix(infile)
|
169
|
+
if available?('gawk') or available?('awk')
|
170
|
+
awk infile, '{ sub(/\r/, ""); printf("%s\n", $0) }'
|
171
|
+
else
|
172
|
+
perl infile, 's/\r\n|\n|\r/\n/g'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.sed(infile, *expr)
|
177
|
+
outfile = tmp_path infile
|
178
|
+
bin = available?('gsed') ? 'gsed' : 'sed'
|
179
|
+
argv = [ bin, expr.map { |e| ['-e', e] } ].flatten
|
180
|
+
spawn argv, :read_from => infile, :write_to => outfile
|
181
|
+
outfile
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.tail(infile, lines)
|
185
|
+
outfile = tmp_path infile
|
186
|
+
argv = ['tail', '-n', lines.to_s, infile]
|
187
|
+
spawn argv, :write_to => outfile
|
188
|
+
outfile
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.head(infile, lines)
|
192
|
+
outfile = tmp_path infile
|
193
|
+
argv = ['head', '-n', lines.to_s, infile]
|
194
|
+
spawn argv, :write_to => outfile
|
195
|
+
outfile
|
196
|
+
end
|
197
|
+
|
198
|
+
# specify character_positions as a string like "3-5" or "3,9-10"
|
199
|
+
def self.cut(infile, character_positions)
|
200
|
+
outfile = tmp_path infile
|
201
|
+
argv = ['cut', '-c', character_positions, infile]
|
202
|
+
spawn argv, :write_to => outfile
|
203
|
+
outfile
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.iconv(infile, to, from)
|
207
|
+
outfile = tmp_path infile
|
208
|
+
argv = ['iconv', '-t', to, '-f', from, infile]
|
209
|
+
spawn argv, :write_to => outfile
|
210
|
+
outfile
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.available?(bin) # :nodoc:
|
214
|
+
bin = bin.to_s
|
215
|
+
return @@available_query[bin] if defined?(@@available_query) and @@available_query.is_a?(::Hash) and @@available_query.has_key?(bin)
|
216
|
+
@@available_query ||= {}
|
217
|
+
@@available_query[bin] = ::Kernel.system 'which', '-s', bin
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.tmp_path(ancestor, extname = nil) # :nodoc:
|
221
|
+
extname ||= ::File.extname ancestor
|
222
|
+
basename = ::File.basename ancestor.sub(/^unix_utils-[0-9]+-/, '').gsub(/\W+/, '_')
|
223
|
+
name = basename + extname
|
224
|
+
::Kernel.srand
|
225
|
+
::File.join ::Dir.tmpdir, "unix_utils-#{::Kernel.rand(1e11)}-#{name}"
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.spawn(argv, options = {}) # :nodoc:
|
229
|
+
if options[:chdir]
|
230
|
+
old_pwd = ::Dir.pwd
|
231
|
+
::Dir.chdir options[:chdir]
|
232
|
+
end
|
233
|
+
|
234
|
+
whole_stdout = nil
|
235
|
+
whole_stderr = nil
|
236
|
+
|
237
|
+
::Open3.popen3(*argv) do |stdin, stdout, stderr|
|
238
|
+
# deal with STDIN
|
239
|
+
if options[:read_from]
|
240
|
+
::File.open(options[:read_from], 'r') do |in_f|
|
241
|
+
while chunk = in_f.read(4_194_304)
|
242
|
+
stdin.write chunk
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
stdin.close
|
247
|
+
|
248
|
+
# deal with STDOUT
|
249
|
+
if options[:write_to]
|
250
|
+
::File.open(options[:write_to], 'wb') do |out_f|
|
251
|
+
while chunk = stdout.read(4_194_304)
|
252
|
+
out_f.write chunk
|
253
|
+
end
|
254
|
+
end
|
255
|
+
whole_stdout = "Redirected to #{options[:write_to]}"
|
256
|
+
else
|
257
|
+
whole_stdout = stdout.read
|
258
|
+
end
|
259
|
+
|
260
|
+
# deal with STDERR
|
261
|
+
whole_stderr = stderr.read
|
262
|
+
end
|
263
|
+
|
264
|
+
unless whole_stderr.empty?
|
265
|
+
$stderr.puts "[unix_utils] `#{argv.join(' ')}` STDERR:"
|
266
|
+
$stderr.puts whole_stderr
|
267
|
+
end
|
268
|
+
|
269
|
+
whole_stdout
|
270
|
+
|
271
|
+
ensure
|
272
|
+
if options[:chdir]
|
273
|
+
::Dir.chdir old_pwd
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'minitest/spec'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'minitest/reporters'
|
7
|
+
MiniTest::Unit.runner = MiniTest::SuiteRunner.new
|
8
|
+
MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
11
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
12
|
+
require 'unix_utils'
|
13
|
+
|
14
|
+
require 'fileutils'
|
15
|
+
require 'tmpdir'
|
16
|
+
require 'tempfile'
|
17
|
+
|
18
|
+
module TestHelper
|
19
|
+
def assert_does_not_touch(method_id, *args)
|
20
|
+
infile_or_srcdir = args.first
|
21
|
+
mtime = File.mtime infile_or_srcdir
|
22
|
+
kind = File.file?(infile_or_srcdir) ? :file : :directory
|
23
|
+
case kind
|
24
|
+
when :file
|
25
|
+
checksum = UnixUtils.shasum infile_or_srcdir, 256
|
26
|
+
when :directory
|
27
|
+
size = UnixUtils.du infile_or_srcdir
|
28
|
+
end
|
29
|
+
destdir = UnixUtils.send(*([method_id] + args))
|
30
|
+
safe_delete destdir
|
31
|
+
File.mtime(infile_or_srcdir).must_equal mtime
|
32
|
+
case kind
|
33
|
+
when :file
|
34
|
+
UnixUtils.shasum(infile_or_srcdir, 256).must_equal checksum
|
35
|
+
when :directory
|
36
|
+
UnixUtils.du(infile_or_srcdir).must_equal size
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def assert_unpack_dir(method_id, infile)
|
41
|
+
destdir = UnixUtils.send method_id, infile
|
42
|
+
File.directory?(destdir).must_equal true
|
43
|
+
Dir.entries(destdir).must_equal %w{ . .. hello_world.txt hello_world.xml }
|
44
|
+
File.dirname(destdir).start_with?(Dir.tmpdir).must_equal true
|
45
|
+
safe_delete destdir
|
46
|
+
end
|
47
|
+
|
48
|
+
def assert_unpack_file(method_id, infile)
|
49
|
+
outfile = UnixUtils.send method_id, infile
|
50
|
+
File.file?(outfile).must_equal true
|
51
|
+
`file #{outfile}`.chomp.must_match %r{text}
|
52
|
+
File.dirname(outfile).start_with?(Dir.tmpdir).must_equal true
|
53
|
+
safe_delete outfile
|
54
|
+
end
|
55
|
+
|
56
|
+
def assert_pack(method_id, infile)
|
57
|
+
outfile = UnixUtils.send method_id, infile
|
58
|
+
File.file?(outfile).must_equal true
|
59
|
+
`file #{outfile}`.chomp.must_match %r{\b#{method_id.to_s.downcase}\b}
|
60
|
+
File.dirname(outfile).start_with?(Dir.tmpdir).must_equal true
|
61
|
+
safe_delete outfile
|
62
|
+
end
|
63
|
+
|
64
|
+
def safe_delete(path)
|
65
|
+
path = File.expand_path path
|
66
|
+
raise "Refusing to rm -rf #{path} because it's not in #{Dir.tmpdir}" unless File.dirname(path).start_with?(Dir.tmpdir)
|
67
|
+
FileUtils.rm_rf path
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class MiniTest::Spec
|
72
|
+
include TestHelper
|
73
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
hello world
|
@@ -0,0 +1 @@
|
|
1
|
+
<b>hello world</b>
|
Binary file
|
Binary file
|
Binary file
|
data/test/target/file.gz
ADDED
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Hola, �c�mo est�s?
|
@@ -0,0 +1 @@
|
|
1
|
+
Hola, ¿cómo estás?
|
@@ -0,0 +1,383 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
describe UnixUtils do
|
5
|
+
before do
|
6
|
+
@old_pwd = Dir.pwd
|
7
|
+
Dir.chdir File.expand_path('../target', __FILE__)
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
Dir.chdir @old_pwd
|
12
|
+
end
|
13
|
+
|
14
|
+
describe :curl do
|
15
|
+
it "downloads to a temp file" do
|
16
|
+
outfile = UnixUtils.curl('http://brighterplanet.com')
|
17
|
+
File.read(outfile).must_match %r{sustain}i
|
18
|
+
safe_delete outfile
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe :shasum do
|
23
|
+
it "checksums a file with SHA-1" do
|
24
|
+
UnixUtils.shasum('directory.zip', 1).must_equal 'c0abb36c923ed7bf87ebb8d7097cb8e264e528d2'
|
25
|
+
end
|
26
|
+
it "checksums a file with SHA-256" do
|
27
|
+
UnixUtils.shasum('directory.zip', 256).must_equal '661af2b7b0993088263228b071b649a88d82a6a655562162c32307d1e127f27a'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe :md5sum do
|
32
|
+
it "checksums a file" do
|
33
|
+
UnixUtils.md5sum('directory.zip').must_equal 'd6e15da798ae19551da6c49ec09afaef'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe :du do
|
38
|
+
it "calculates the size of a directory in bytes" do
|
39
|
+
UnixUtils.du('directory').must_equal 16
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe :unzip do
|
44
|
+
before do
|
45
|
+
@infile = 'directory.zip'
|
46
|
+
@anonymous_infile = 'directory-really-a-z_i_p-shh'
|
47
|
+
end
|
48
|
+
it "unpacks a DIRECTORY located in the tmp directory" do
|
49
|
+
assert_unpack_dir :unzip, @infile
|
50
|
+
end
|
51
|
+
it "accepts unsemantic filenames" do
|
52
|
+
assert_unpack_dir :unzip, @anonymous_infile
|
53
|
+
end
|
54
|
+
it "does not touch the infile" do
|
55
|
+
assert_does_not_touch :unzip, @infile
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe :untar do
|
60
|
+
before do
|
61
|
+
@infile = 'directory.tar'
|
62
|
+
@anonymous_infile = 'directory-really-a-t_a_r-shh'
|
63
|
+
end
|
64
|
+
it "unpacks a DIRECTORY located in the tmp directory" do
|
65
|
+
assert_unpack_dir :untar, @infile
|
66
|
+
end
|
67
|
+
it "accepts unsemantic filenames" do
|
68
|
+
assert_unpack_dir :untar, @anonymous_infile
|
69
|
+
end
|
70
|
+
it "does not touch the infile" do
|
71
|
+
assert_does_not_touch :untar, @infile
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe :bunzip2 do
|
76
|
+
before do
|
77
|
+
@infile = 'file.bz2'
|
78
|
+
@anonymous_infile = 'file-really-a-b_z_2-shh'
|
79
|
+
end
|
80
|
+
it "unpacks a FILE located in the tmp directory" do
|
81
|
+
assert_unpack_file :bunzip2, @infile
|
82
|
+
end
|
83
|
+
it "accepts unsemantic filenames" do
|
84
|
+
assert_unpack_file :bunzip2, @anonymous_infile
|
85
|
+
end
|
86
|
+
it "does not touch the infile" do
|
87
|
+
assert_does_not_touch :bunzip2, @infile
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe :gunzip do
|
92
|
+
before do
|
93
|
+
@infile = 'file.gz'
|
94
|
+
@anonymous_infile = 'file-really-a-g_z-shh'
|
95
|
+
end
|
96
|
+
it "unpacks a FILE located in the tmp directory" do
|
97
|
+
assert_unpack_file :gunzip, @infile
|
98
|
+
end
|
99
|
+
it "accepts unsemantic filenames" do
|
100
|
+
assert_unpack_file :gunzip, @anonymous_infile
|
101
|
+
end
|
102
|
+
it "does not touch the infile" do
|
103
|
+
assert_does_not_touch :gunzip, @infile
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe :bzip2 do
|
108
|
+
before do
|
109
|
+
@infile = 'directory.tar'
|
110
|
+
end
|
111
|
+
it "packs a FILE to a FILE in the tmp directory" do
|
112
|
+
assert_pack :bzip2, @infile
|
113
|
+
end
|
114
|
+
it "does not touch the infile" do
|
115
|
+
assert_does_not_touch :bzip2, @infile
|
116
|
+
end
|
117
|
+
it "sticks on a useful extension" do
|
118
|
+
outfile = UnixUtils.bzip2 @infile
|
119
|
+
File.extname(outfile).must_equal '.bz2'
|
120
|
+
safe_delete outfile
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe :gzip do
|
125
|
+
before do
|
126
|
+
@infile = 'directory.tar'
|
127
|
+
end
|
128
|
+
it "packs a FILE to a FILE in the tmp directory" do
|
129
|
+
assert_pack :gzip, @infile
|
130
|
+
end
|
131
|
+
it "does not touch the infile" do
|
132
|
+
assert_does_not_touch :gzip, @infile
|
133
|
+
end
|
134
|
+
it "sticks on a useful extension" do
|
135
|
+
outfile = UnixUtils.gzip @infile
|
136
|
+
File.extname(outfile).must_equal '.gz'
|
137
|
+
safe_delete outfile
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe :zip do
|
142
|
+
before do
|
143
|
+
@srcdir = 'directory'
|
144
|
+
end
|
145
|
+
it "packs a DIRECTORY to a FILE in the tmp directory" do
|
146
|
+
assert_pack :zip, @srcdir
|
147
|
+
end
|
148
|
+
it "does not touch the infile" do
|
149
|
+
assert_does_not_touch :zip, @srcdir
|
150
|
+
end
|
151
|
+
it "sticks on a useful extension" do
|
152
|
+
outfile = UnixUtils.zip @srcdir
|
153
|
+
File.extname(outfile).must_equal '.zip'
|
154
|
+
safe_delete outfile
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe :tar do
|
159
|
+
before do
|
160
|
+
@srcdir = 'directory'
|
161
|
+
end
|
162
|
+
it "packs a DIRECTORY to a FILE in the tmp directory" do
|
163
|
+
assert_pack :tar, @srcdir
|
164
|
+
end
|
165
|
+
it "does not touch the infile" do
|
166
|
+
assert_does_not_touch :tar, @srcdir
|
167
|
+
end
|
168
|
+
it "sticks on a useful extension" do
|
169
|
+
outfile = UnixUtils.tar @srcdir
|
170
|
+
File.extname(outfile).must_equal '.tar'
|
171
|
+
safe_delete outfile
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe :perl do
|
176
|
+
before do
|
177
|
+
@f = Tempfile.new('perl.txt')
|
178
|
+
@f.write "bad\n"*10
|
179
|
+
@f.flush
|
180
|
+
@infile = @f.path
|
181
|
+
end
|
182
|
+
after do
|
183
|
+
@f.close
|
184
|
+
end
|
185
|
+
it "processes a file" do
|
186
|
+
outfile = UnixUtils.perl(@infile, 's/bad/good/g')
|
187
|
+
File.read(outfile).must_equal "good\n"*10
|
188
|
+
safe_delete outfile
|
189
|
+
end
|
190
|
+
it "does not touch the infile" do
|
191
|
+
assert_does_not_touch :perl, @infile, 's/bad/good/g'
|
192
|
+
end
|
193
|
+
it "keeps the original extname" do
|
194
|
+
outfile = UnixUtils.perl(@infile, 's/bad/good/g')
|
195
|
+
File.extname(outfile).must_equal File.extname(@infile)
|
196
|
+
safe_delete outfile
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe :awk do
|
201
|
+
before do
|
202
|
+
@f = Tempfile.new('awk.txt')
|
203
|
+
@f.write "bad\n"*10
|
204
|
+
@f.flush
|
205
|
+
@infile = @f.path
|
206
|
+
end
|
207
|
+
after do
|
208
|
+
@f.close
|
209
|
+
end
|
210
|
+
it "processes a file" do
|
211
|
+
outfile = UnixUtils.awk(@infile, '{gsub(/bad/, "good"); print}')
|
212
|
+
File.read(outfile).must_equal "good\n"*10
|
213
|
+
safe_delete outfile
|
214
|
+
end
|
215
|
+
it "does not touch the infile" do
|
216
|
+
assert_does_not_touch :awk, @infile, '{gsub(/bad/, "good"); print}'
|
217
|
+
end
|
218
|
+
it "keeps the original extname" do
|
219
|
+
outfile = UnixUtils.awk(@infile, '{gsub(/bad/, "good"); print}')
|
220
|
+
File.extname(outfile).must_equal File.extname(@infile)
|
221
|
+
safe_delete outfile
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe :unix2dos do
|
226
|
+
before do
|
227
|
+
@f = Tempfile.new('unix2dos.txt')
|
228
|
+
@f.write "unix\n"*5
|
229
|
+
@f.write "dos\r\n"*5
|
230
|
+
@f.flush
|
231
|
+
@infile = @f.path
|
232
|
+
end
|
233
|
+
after do
|
234
|
+
@f.close
|
235
|
+
end
|
236
|
+
it 'converts newlines' do
|
237
|
+
outfile = UnixUtils.unix2dos @infile
|
238
|
+
File.read(outfile).must_equal("unix\r\n"*5 + "dos\r\n"*5)
|
239
|
+
safe_delete outfile
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe :dos2unix do
|
244
|
+
before do
|
245
|
+
@f = Tempfile.new('dos2unix.txt')
|
246
|
+
@f.write "dos\r\n"*5
|
247
|
+
@f.write "unix\n"*5
|
248
|
+
@f.flush
|
249
|
+
@infile = @f.path
|
250
|
+
end
|
251
|
+
after do
|
252
|
+
@f.close
|
253
|
+
end
|
254
|
+
it 'converts newlines' do
|
255
|
+
outfile = UnixUtils.dos2unix @infile
|
256
|
+
File.read(outfile).must_equal("dos\n"*5 + "unix\n"*5)
|
257
|
+
safe_delete outfile
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe :wc do
|
262
|
+
before do
|
263
|
+
@f = Tempfile.new('wc.txt')
|
264
|
+
@f.write "dos line\r\n"*5
|
265
|
+
@f.write "unix line\n"*5
|
266
|
+
@f.flush
|
267
|
+
@infile = @f.path
|
268
|
+
end
|
269
|
+
after do
|
270
|
+
@f.close
|
271
|
+
end
|
272
|
+
it 'counts lines, words, and bytes' do
|
273
|
+
UnixUtils.wc(@infile).must_equal [5+5, 10+10, 50+50]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe :sed do
|
278
|
+
before do
|
279
|
+
@f = Tempfile.new('sed.txt')
|
280
|
+
@f.write "bad\n"*10
|
281
|
+
@f.flush
|
282
|
+
@infile = @f.path
|
283
|
+
end
|
284
|
+
after do
|
285
|
+
@f.close
|
286
|
+
end
|
287
|
+
it "processes a file" do
|
288
|
+
outfile = UnixUtils.sed(@infile, 's/bad/good/g')
|
289
|
+
File.read(outfile).must_equal "good\n"*10
|
290
|
+
safe_delete outfile
|
291
|
+
end
|
292
|
+
it "does not touch the infile" do
|
293
|
+
assert_does_not_touch :sed, @infile, 's/bad/good/g'
|
294
|
+
end
|
295
|
+
it "keeps the original extname" do
|
296
|
+
outfile = UnixUtils.sed(@infile, 's/bad/good/g')
|
297
|
+
File.extname(outfile).must_equal File.extname(@infile)
|
298
|
+
safe_delete outfile
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
describe :tail do
|
304
|
+
before do
|
305
|
+
@a2z = ('a'..'z').to_a
|
306
|
+
@f = Tempfile.new('tail.txt')
|
307
|
+
@f.write @a2z.join("\n")
|
308
|
+
@f.flush
|
309
|
+
@infile = @f.path
|
310
|
+
end
|
311
|
+
after do
|
312
|
+
@f.close
|
313
|
+
end
|
314
|
+
it 'gets last three lines' do
|
315
|
+
outfile = UnixUtils.tail(@infile, 3)
|
316
|
+
File.read(outfile).must_equal @a2z.last(3).join("\n")
|
317
|
+
safe_delete outfile
|
318
|
+
end
|
319
|
+
it 'gets trailing lines starting with the third line (inclusive)' do
|
320
|
+
outfile = UnixUtils.tail(@infile, '+3')
|
321
|
+
File.read(outfile).must_equal @a2z[2..-1].join("\n")
|
322
|
+
safe_delete outfile
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
describe :head do
|
327
|
+
before do
|
328
|
+
@a2z = ('a'..'z').to_a
|
329
|
+
@f = Tempfile.new('head.txt')
|
330
|
+
@f.write @a2z.join("\n")
|
331
|
+
@f.flush
|
332
|
+
@infile = @f.path
|
333
|
+
end
|
334
|
+
after do
|
335
|
+
@f.close
|
336
|
+
end
|
337
|
+
it 'gets first three lines' do
|
338
|
+
outfile = UnixUtils.head(@infile, 3)
|
339
|
+
File.read(outfile).must_equal(@a2z.first(3).join("\n") + "\n")
|
340
|
+
safe_delete outfile
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
describe :cut do
|
345
|
+
before do
|
346
|
+
@a2z = ('a'..'z').to_a
|
347
|
+
@f = Tempfile.new('cut.txt')
|
348
|
+
10.times do
|
349
|
+
@f.write(@a2z.join + "\n")
|
350
|
+
end
|
351
|
+
@f.flush
|
352
|
+
@infile = @f.path
|
353
|
+
end
|
354
|
+
after do
|
355
|
+
@f.close
|
356
|
+
end
|
357
|
+
it 'cuts out character positions' do
|
358
|
+
outfile = UnixUtils.cut(@infile, '1,12,13,15,19,20')
|
359
|
+
almosts = (0..9).map { |i| 'almost' }.join("\n") + "\n"
|
360
|
+
File.read(outfile).must_equal almosts
|
361
|
+
safe_delete outfile
|
362
|
+
end
|
363
|
+
it 'cuts out character ranges (inclusive)' do
|
364
|
+
outfile = UnixUtils.cut(@infile, '3-6')
|
365
|
+
cdefs = (0..9).map { |i| 'cdef' }.join("\n") + "\n"
|
366
|
+
File.read(outfile).must_equal cdefs
|
367
|
+
safe_delete outfile
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
describe :iconv do
|
372
|
+
it 'converts files from utf-8 to latin1' do
|
373
|
+
outfile = UnixUtils.iconv('utf8.txt', 'ISO-8859-1', 'UTF-8')
|
374
|
+
UnixUtils.md5sum(outfile).must_equal UnixUtils.md5sum('iso-8859-1.txt')
|
375
|
+
safe_delete outfile
|
376
|
+
end
|
377
|
+
it 'converts files from latin1 to utf-8' do
|
378
|
+
outfile = UnixUtils.iconv('iso-8859-1.txt', 'UTF-8', 'ISO-8859-1')
|
379
|
+
UnixUtils.md5sum(outfile).must_equal UnixUtils.md5sum('utf8.txt')
|
380
|
+
safe_delete outfile
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
data/unix_utils.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/unix_utils/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Seamus Abshere"]
|
6
|
+
gem.email = ["seamus@abshere.net"]
|
7
|
+
desc = %q{Like FileUtils, but provides zip, unzip, bzip2, bunzip2, tar, untar, sed, du, md5sum, shasum, cut, head, tail, wc, unix2dos, dos2unix, iconv, curl, perl, etc.}
|
8
|
+
gem.description = desc
|
9
|
+
gem.summary = desc
|
10
|
+
gem.homepage = "https://github.com/seamusabshere/unix_utils"
|
11
|
+
|
12
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
13
|
+
gem.files = `git ls-files`.split("\n")
|
14
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
gem.name = "unix_utils"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = UnixUtils::VERSION
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unix_utils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Seamus Abshere
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-17 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Like FileUtils, but provides zip, unzip, bzip2, bunzip2, tar, untar,
|
15
|
+
sed, du, md5sum, shasum, cut, head, tail, wc, unix2dos, dos2unix, iconv, curl, perl,
|
16
|
+
etc.
|
17
|
+
email:
|
18
|
+
- seamus@abshere.net
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- .gitignore
|
24
|
+
- Gemfile
|
25
|
+
- History.txt
|
26
|
+
- README.markdown
|
27
|
+
- Rakefile
|
28
|
+
- lib/unix_utils.rb
|
29
|
+
- lib/unix_utils/version.rb
|
30
|
+
- test/helper.rb
|
31
|
+
- test/target/directory-really-a-t_a_r-shh
|
32
|
+
- test/target/directory-really-a-z_i_p-shh
|
33
|
+
- test/target/directory.tar
|
34
|
+
- test/target/directory.zip
|
35
|
+
- test/target/directory/hello_world.txt
|
36
|
+
- test/target/directory/hello_world.xml
|
37
|
+
- test/target/file-really-a-b_z_2-shh
|
38
|
+
- test/target/file-really-a-g_z-shh
|
39
|
+
- test/target/file.bz2
|
40
|
+
- test/target/file.gz
|
41
|
+
- test/target/iso-8859-1.txt
|
42
|
+
- test/target/utf8.txt
|
43
|
+
- test/test_unix_utils.rb
|
44
|
+
- unix_utils.gemspec
|
45
|
+
homepage: https://github.com/seamusabshere/unix_utils
|
46
|
+
licenses: []
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.8.15
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Like FileUtils, but provides zip, unzip, bzip2, bunzip2, tar, untar, sed,
|
69
|
+
du, md5sum, shasum, cut, head, tail, wc, unix2dos, dos2unix, iconv, curl, perl,
|
70
|
+
etc.
|
71
|
+
test_files:
|
72
|
+
- test/helper.rb
|
73
|
+
- test/target/directory-really-a-t_a_r-shh
|
74
|
+
- test/target/directory-really-a-z_i_p-shh
|
75
|
+
- test/target/directory.tar
|
76
|
+
- test/target/directory.zip
|
77
|
+
- test/target/directory/hello_world.txt
|
78
|
+
- test/target/directory/hello_world.xml
|
79
|
+
- test/target/file-really-a-b_z_2-shh
|
80
|
+
- test/target/file-really-a-g_z-shh
|
81
|
+
- test/target/file.bz2
|
82
|
+
- test/target/file.gz
|
83
|
+
- test/target/iso-8859-1.txt
|
84
|
+
- test/target/utf8.txt
|
85
|
+
- test/test_unix_utils.rb
|
86
|
+
has_rdoc:
|