unix_utils 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|