ptools 1.1.7 → 1.1.9
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/CHANGES +20 -0
- data/MANIFEST +0 -2
- data/README +35 -47
- data/Rakefile +110 -0
- data/lib/ptools.rb +301 -231
- data/ptools.gemspec +31 -0
- data/test/test_binary.rb +7 -2
- data/test/test_constants.rb +1 -1
- data/test/test_head.rb +7 -2
- data/test/test_image.rb +6 -1
- data/test/test_middle.rb +2 -1
- data/test/test_nlconvert.rb +13 -9
- data/test/test_null.rb +5 -0
- data/test/test_tail.rb +6 -1
- data/test/test_touch.rb +6 -1
- data/test/test_wc.rb +5 -0
- data/test/test_whereis.rb +83 -37
- data/test/test_which.rb +90 -32
- metadata +27 -18
- data/test/test_file1.txt +0 -25
- data/test/test_file2.txt +0 -25
data/CHANGES
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
== 1.1.9 - 25-Mar-2010
|
2
|
+
* Refactored File.which and File.whereis and added additional tests for each.
|
3
|
+
* Removed the block form of File.whereis.
|
4
|
+
* Reorganized the Rakefile a bit and put the test tasks under the 'test'
|
5
|
+
namespace, and the gem tasks under the 'gem' namespace.
|
6
|
+
* Updated the test-unit development dependency.
|
7
|
+
* Updates the README.
|
8
|
+
|
9
|
+
== 1.1.8 - 6-Oct-2009
|
10
|
+
* Fixed some bad variable names that crept into the File.nl_convert method.
|
11
|
+
* Added the File.null_device alias for File.null.
|
12
|
+
* Added cygwin and mingw to Windows detection code.
|
13
|
+
* One of the nlconvert tests is now skipped on Windows.
|
14
|
+
* Two text files that were previously bundled as part of this library for
|
15
|
+
testing purposes have been removed. Those are now dynamically generated
|
16
|
+
within the tests themselves.
|
17
|
+
* Gemspec updates.
|
18
|
+
* Documentation updates.
|
19
|
+
* Added the :gem rake task.
|
20
|
+
|
1
21
|
== 1.1.7 - 28-Jul-2009
|
2
22
|
* Now compatible with Ruby 1.9.x.
|
3
23
|
* Replaced RUBY_PLATFORM with rbconfig check for the sake of other
|
data/MANIFEST
CHANGED
data/README
CHANGED
@@ -1,76 +1,64 @@
|
|
1
1
|
== Description
|
2
|
-
|
3
|
-
|
2
|
+
The ptools (power tools) library is an additional set of commands for the
|
3
|
+
File class based on Unix command line tools.
|
4
4
|
|
5
5
|
== Prerequisites
|
6
|
-
|
7
|
-
On MS Windows you will need win32-file 0.5.4 or later.
|
6
|
+
On MS Windows you will need win32-file 0.5.4 or later.
|
8
7
|
|
9
8
|
== Installation
|
10
|
-
|
11
|
-
ruby ptools.gemspec
|
12
|
-
gem install ptools-x.y.z.gem
|
13
|
-
=== Manual
|
14
|
-
rake test (optional)
|
15
|
-
rake install
|
9
|
+
gem install ptools
|
16
10
|
|
17
11
|
== Synopsis
|
18
|
-
|
12
|
+
require "ptools"
|
19
13
|
|
20
|
-
|
21
|
-
|
14
|
+
File.which("ruby") # '/usr/local/bin/ruby'
|
15
|
+
File.whereis("ruby") # ['/usr/local/bin/ruby','/opt/bin/ruby']
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
17
|
+
File.head("myfile") # Returns first 10 lines of 'myfile'
|
18
|
+
File.middle("myfile",8,12) # Returns lines 8-12 of 'myfile'
|
19
|
+
File.tail("myfile",3) # Returns last 3 lines of 'myfile'
|
20
|
+
File.wc("myfile",'words') # Returns the number of words in 'myfile'
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
File.touch("newfile") # "newfile" now exists
|
23
|
+
File.null # '/dev/null' on Unix, 'NUL' on Windows
|
24
|
+
File.binary?('some_file') # true or false
|
31
25
|
|
32
|
-
|
33
|
-
|
26
|
+
# Creates a copy of 'myfile' called 'newfile', in DOS format
|
27
|
+
File.nl_convert("myfile", "newfile", "dos")
|
34
28
|
|
35
29
|
== Known Bugs
|
36
|
-
|
37
|
-
converting to DOS or MAC format if nl_convert is run multiple times on
|
38
|
-
the same file. This appears to be fixed in 1.8.x. You're not still running
|
39
|
-
Ruby 1.6.x are you? I would hope not.
|
30
|
+
None known. Please report any bugs on the github or RubyForge project page.
|
40
31
|
|
41
|
-
|
42
|
-
|
32
|
+
http://www.rubyforge.org/projects/shards
|
33
|
+
http://www.github.com/djberg96/ptools
|
43
34
|
|
44
35
|
== Acknowledgements
|
45
|
-
|
46
|
-
|
47
|
-
|
36
|
+
The File.which method was originally adopted from the FileWhich code posted
|
37
|
+
by Michael Granger on the now defunct rubygarden.org website. That code was
|
38
|
+
later replaced by a version based on the ruby-which library.
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
Tom Christiansen.
|
40
|
+
The File.nl_convert method is based on the nlcvt program found at
|
41
|
+
http://www.perl.com/language/ppt/src/nlcvt/nlcvt, written by Tom Christiansen.
|
52
42
|
|
53
|
-
|
43
|
+
The middle() method was provided by Shashank Date.
|
54
44
|
|
55
|
-
|
56
|
-
|
45
|
+
The binary?() method was based almost entirely on a blog post by Ryan
|
46
|
+
Davis (who, in turn, based his code on Perl's -B switch).
|
57
47
|
|
58
48
|
== Future Plans
|
59
|
-
|
49
|
+
Add whatever other tools people think might be useful.
|
60
50
|
|
61
51
|
== License
|
62
|
-
|
52
|
+
Artistic 2.0
|
63
53
|
|
64
54
|
== Copyright
|
65
|
-
|
66
|
-
|
55
|
+
(C) 2003-2010 Daniel J. Berger
|
56
|
+
All Rights Reserved.
|
67
57
|
|
68
58
|
== Warranty
|
69
|
-
|
70
|
-
|
71
|
-
|
59
|
+
This package is provided "as is" and without any express or
|
60
|
+
implied warranties, including, without limitation, the implied
|
61
|
+
warranties of merchantability and fitness for a particular purpose.
|
72
62
|
|
73
63
|
== Author
|
74
|
-
|
75
|
-
djberg96 at nospam at gmail dot com
|
76
|
-
imperator on IRC (irc.freenode.net)
|
64
|
+
Daniel J. Berger
|
data/Rakefile
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rbconfig'
|
4
|
+
include Config
|
5
|
+
|
6
|
+
desc 'Install the ptools package (non-gem)'
|
7
|
+
task :install do
|
8
|
+
sitelibdir = CONFIG["sitelibdir"]
|
9
|
+
file = "lib/ptools.rb"
|
10
|
+
FileUtils.cp(file, sitelibdir, :verbose => true)
|
11
|
+
end
|
12
|
+
|
13
|
+
namespace 'gem' do
|
14
|
+
desc 'Create the ptools gem'
|
15
|
+
task :create do
|
16
|
+
Dir["*.gem"].each{ |f| File.delete(f) } # Clean first
|
17
|
+
spec = eval(IO.read('ptools.gemspec'))
|
18
|
+
Gem::Builder.new(spec).build
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Install the ptools gem'
|
22
|
+
task :install => [:create] do
|
23
|
+
file = Dir["*.gem"].first
|
24
|
+
sh "gem install #{file}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Rake::TestTask.new do |t|
|
29
|
+
t.verbose = true
|
30
|
+
t.warning = true
|
31
|
+
end
|
32
|
+
|
33
|
+
namespace 'test' do
|
34
|
+
Rake::TestTask.new('binary') do |t|
|
35
|
+
t.libs << 'test'
|
36
|
+
t.verbose = true
|
37
|
+
t.warning = true
|
38
|
+
t.test_files = FileList['test/test_binary.rb']
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::TestTask.new('constants') do |t|
|
42
|
+
t.libs << 'test'
|
43
|
+
t.verbose = true
|
44
|
+
t.warning = true
|
45
|
+
t.test_files = FileList['test/test_constants.rb']
|
46
|
+
end
|
47
|
+
|
48
|
+
Rake::TestTask.new('head') do |t|
|
49
|
+
t.libs << 'test'
|
50
|
+
t.verbose = true
|
51
|
+
t.warning = true
|
52
|
+
t.test_files = FileList['test/test_head.rb']
|
53
|
+
end
|
54
|
+
|
55
|
+
Rake::TestTask.new('middle') do |t|
|
56
|
+
t.libs << 'test'
|
57
|
+
t.verbose = true
|
58
|
+
t.warning = true
|
59
|
+
t.test_files = FileList['test/test_middle.rb']
|
60
|
+
end
|
61
|
+
|
62
|
+
Rake::TestTask.new('nlconvert') do |t|
|
63
|
+
t.libs << 'test'
|
64
|
+
t.verbose = true
|
65
|
+
t.warning = true
|
66
|
+
t.test_files = FileList['test/test_nlconvert.rb']
|
67
|
+
end
|
68
|
+
|
69
|
+
Rake::TestTask.new('null') do |t|
|
70
|
+
t.libs << 'test'
|
71
|
+
t.verbose = true
|
72
|
+
t.warning = true
|
73
|
+
t.test_files = FileList['test/test_null.rb']
|
74
|
+
end
|
75
|
+
|
76
|
+
Rake::TestTask.new('tail') do |t|
|
77
|
+
t.libs << 'test'
|
78
|
+
t.verbose = true
|
79
|
+
t.warning = true
|
80
|
+
t.test_files = FileList['test/test_tail.rb']
|
81
|
+
end
|
82
|
+
|
83
|
+
Rake::TestTask.new('touch') do |t|
|
84
|
+
t.libs << 'test'
|
85
|
+
t.verbose = true
|
86
|
+
t.warning = true
|
87
|
+
t.test_files = FileList['test/test_touch.rb']
|
88
|
+
end
|
89
|
+
|
90
|
+
Rake::TestTask.new('wc') do |t|
|
91
|
+
t.libs << 'test'
|
92
|
+
t.verbose = true
|
93
|
+
t.warning = true
|
94
|
+
t.test_files = FileList['test/test_wc.rb']
|
95
|
+
end
|
96
|
+
|
97
|
+
Rake::TestTask.new('whereis') do |t|
|
98
|
+
t.libs << 'test'
|
99
|
+
t.verbose = true
|
100
|
+
t.warning = true
|
101
|
+
t.test_files = FileList['test/test_whereis.rb']
|
102
|
+
end
|
103
|
+
|
104
|
+
Rake::TestTask.new('which') do |t|
|
105
|
+
t.libs << 'test'
|
106
|
+
t.verbose = true
|
107
|
+
t.warning = true
|
108
|
+
t.test_files = FileList['test/test_which.rb']
|
109
|
+
end
|
110
|
+
end
|
data/lib/ptools.rb
CHANGED
@@ -1,152 +1,218 @@
|
|
1
1
|
require 'rbconfig'
|
2
|
-
|
2
|
+
|
3
|
+
if Config::CONFIG['host_os'] =~ /mswin|dos|win32|cygwin|mingw/i
|
4
|
+
require 'win32/file'
|
5
|
+
end
|
3
6
|
|
4
7
|
class File
|
5
|
-
|
8
|
+
# The version of the ptools library.
|
9
|
+
PTOOLS_VERSION = '1.1.9'
|
10
|
+
|
11
|
+
# :stopdoc:
|
12
|
+
|
13
|
+
# The WIN32EXTS string is used as part of a Dir[] call in certain methods.
|
14
|
+
if Config::CONFIG['host_os'] =~ /mswin|dos|win32|cygwin|mingw/i
|
15
|
+
MSWINDOWS = true
|
16
|
+
if ENV['PATHEXT']
|
17
|
+
WIN32EXTS = ('.{' + ENV['PATHEXT'].tr(';', ',').tr('.','') + '}').downcase
|
18
|
+
else
|
19
|
+
WIN32EXTS = '.{exe,com,bat}'
|
20
|
+
end
|
21
|
+
else
|
22
|
+
MSWINDOWS = false
|
23
|
+
end
|
24
|
+
|
25
|
+
IMAGE_EXT = %w/.bmp .gif .jpg .jpeg .png/
|
26
|
+
|
27
|
+
# :startdoc:
|
28
|
+
|
29
|
+
# Returns whether or not the file is an image. Only JPEG, PNG, BMP and
|
30
|
+
# GIF are checked against.
|
31
|
+
#
|
32
|
+
# This method does some simple read and extension checks. For a version
|
33
|
+
# that is more robust, but which depends on a 3rd party C library (and is
|
34
|
+
# difficult to build on MS Windows), see the 'filemagic' library, available
|
35
|
+
# on the RAA.
|
36
|
+
#
|
37
|
+
# Examples:
|
38
|
+
#
|
39
|
+
# File.image?('somefile.jpg') # => true
|
40
|
+
# File.image?('somefile.txt') # => true
|
41
|
+
#--
|
42
|
+
# The approach I used here is based on information found at
|
43
|
+
# http://en.wikipedia.org/wiki/Magic_number_(programming)
|
44
|
+
#
|
45
|
+
def self.image?(file)
|
46
|
+
bool = IMAGE_EXT.include?(File.extname(file).downcase) # Match ext
|
47
|
+
bool = bmp?(file) || jpg?(file) || png?(file) || gif?(file) # Check data
|
48
|
+
bool
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the name of the null device (aka bitbucket) on your platform.
|
52
|
+
#
|
53
|
+
# Examples:
|
54
|
+
#
|
55
|
+
# # On Linux
|
56
|
+
# File.null # => '/dev/null'
|
57
|
+
#
|
58
|
+
# # On MS Windows
|
59
|
+
# File.null # => 'NUL'
|
60
|
+
#--
|
61
|
+
# The values I used here are based on information from
|
62
|
+
# http://en.wikipedia.org/wiki//dev/null
|
63
|
+
#
|
64
|
+
def self.null
|
65
|
+
case Config::CONFIG['host_os']
|
66
|
+
when /mswin|win32|msdos|cygwin|mingw/i
|
67
|
+
'NUL'
|
68
|
+
when /amiga/i
|
69
|
+
'NIL:'
|
70
|
+
when /openvms/i
|
71
|
+
'NL:'
|
72
|
+
else
|
73
|
+
'/dev/null'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class << self
|
78
|
+
alias null_device null
|
79
|
+
end
|
6
80
|
|
7
|
-
|
81
|
+
# Returns whether or not +file+ is a binary file. Note that this is
|
82
|
+
# not guaranteed to be 100% accurate. It performs a "best guess" based
|
83
|
+
# on a simple test of the first +File.blksize+ characters.
|
84
|
+
#
|
85
|
+
# Example:
|
86
|
+
#
|
87
|
+
# File.binary?('somefile.exe') # => true
|
88
|
+
# File.binary?('somefile.txt') # => false
|
89
|
+
#--
|
90
|
+
# Based on code originally provided by Ryan Davis (which, in turn, is
|
91
|
+
# based on Perl's -B switch).
|
92
|
+
#
|
93
|
+
def self.binary?(file)
|
94
|
+
s = (File.read(file, File.stat(file).blksize) || "").split(//)
|
95
|
+
((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
|
96
|
+
end
|
97
|
+
|
98
|
+
# Looks for the first occurrence of +program+ within +path+.
|
99
|
+
#
|
100
|
+
# On Windows, it looks for executables ending with the suffixes defined
|
101
|
+
# in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
|
102
|
+
# that isn't defined, which you may optionally include in +program+.
|
103
|
+
#
|
104
|
+
# Returns nil if not found.
|
105
|
+
#
|
106
|
+
# Examples:
|
107
|
+
#
|
108
|
+
# File.which('ruby') # => '/usr/local/bin/ruby'
|
109
|
+
# File.which('foo') # => nil
|
110
|
+
#
|
111
|
+
def self.which(program, path=ENV['PATH'])
|
112
|
+
if path.nil? || path.empty?
|
113
|
+
raise ArgumentError, "path cannot be empty"
|
114
|
+
end
|
8
115
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
116
|
+
# Bail out early if an absolute path is provided.
|
117
|
+
if program =~ /^\/|^[a-z]:[\\\/]/i
|
118
|
+
program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
|
119
|
+
found = Dir[program].first
|
120
|
+
if found && File.executable?(found) && !File.directory?(found)
|
121
|
+
return found
|
122
|
+
else
|
123
|
+
return nil
|
15
124
|
end
|
16
|
-
|
17
|
-
IS_WINDOWS = false
|
18
|
-
end
|
125
|
+
end
|
19
126
|
|
20
|
-
|
127
|
+
# Iterate over each path glob the dir + program.
|
128
|
+
path.split(File::PATH_SEPARATOR).each{ |dir|
|
129
|
+
next unless File.exists?(dir) # In case of bogus second argument
|
130
|
+
file = File.join(dir, program)
|
21
131
|
|
22
|
-
|
132
|
+
# Dir[] doesn't handle backslashes properly, so convert them. Also, if
|
133
|
+
# the program name doesn't have an extension, try them all.
|
134
|
+
if MSWINDOWS
|
135
|
+
file = file.tr("\\", "/")
|
136
|
+
file += WIN32EXTS if File.extname(program).empty?
|
137
|
+
end
|
23
138
|
|
24
|
-
|
25
|
-
# GIF are checked against.
|
26
|
-
#
|
27
|
-
# This method does some simple read and extension checks. For a version
|
28
|
-
# that is more robust, but which depends on a 3rd party C library (and is
|
29
|
-
# difficult to build on MS Windows), see the 'filemagic' library, available
|
30
|
-
# on the RAA.
|
31
|
-
#--
|
32
|
-
# Approach used here is based on information found at
|
33
|
-
# http://en.wikipedia.org/wiki/Magic_number_(programming)
|
34
|
-
#
|
35
|
-
def self.image?(file)
|
36
|
-
bool = IMAGE_EXT.include?(File.extname(file).downcase) # Match ext
|
37
|
-
bool = bmp?(file) || jpg?(file) || png?(file) || gif?(file) # Check data
|
38
|
-
bool
|
39
|
-
end
|
139
|
+
found = Dir[file].first
|
40
140
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
#
|
46
|
-
def self.null
|
47
|
-
case Config::CONFIG['host_os']
|
48
|
-
when /mswin/i
|
49
|
-
'NUL'
|
50
|
-
when /amiga/i
|
51
|
-
'NIL:'
|
52
|
-
when /openvms/i
|
53
|
-
'NL:'
|
54
|
-
else
|
55
|
-
'/dev/null'
|
141
|
+
# Convert all forward slashes to backslashes if supported
|
142
|
+
if found && File.executable?(found) && !File.directory?(found)
|
143
|
+
found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
|
144
|
+
return found
|
56
145
|
end
|
57
|
-
|
146
|
+
}
|
58
147
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
#
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
if
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns an array of each +program+ within +path+, or nil if it cannot
|
152
|
+
# be found.
|
153
|
+
#
|
154
|
+
# On Windows, it looks for executables ending with the suffixes defined
|
155
|
+
# in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
|
156
|
+
# that isn't defined, which you may optionally include in +program+.
|
157
|
+
#
|
158
|
+
# Examples:
|
159
|
+
#
|
160
|
+
# File.whereis('ruby') # => ['/usr/bin/ruby', '/usr/local/bin/ruby']
|
161
|
+
# File.whereis('foo') # => nil
|
162
|
+
#
|
163
|
+
def self.whereis(program, path=ENV['PATH'])
|
164
|
+
if path.nil? || path.empty?
|
165
|
+
raise ArgumentError, "path cannot be empty"
|
166
|
+
end
|
167
|
+
|
168
|
+
paths = []
|
169
|
+
|
170
|
+
# Bail out early if an absolute path is provided.
|
171
|
+
if program =~ /^\/|^[a-z]:[\\\/]/i
|
172
|
+
program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
|
173
|
+
found = Dir[program]
|
174
|
+
if found[0] && File.executable?(found[0]) && !File.directory?(found[0])
|
175
|
+
return found
|
176
|
+
else
|
177
|
+
return nil
|
89
178
|
end
|
90
|
-
|
91
|
-
# Catch the first path found, or nil
|
92
|
-
location = catch(:done){
|
93
|
-
path.split(File::PATH_SEPARATOR).each{ |dir|
|
94
|
-
programs.each{ |prog|
|
95
|
-
f = File.join(dir, prog)
|
96
|
-
if File.executable?(f) && !File.directory?(f)
|
97
|
-
location = File.join(dir, prog)
|
98
|
-
location.tr!('/', File::ALT_SEPARATOR) if File::ALT_SEPARATOR
|
99
|
-
throw(:done, location)
|
100
|
-
end
|
101
|
-
}
|
102
|
-
}
|
103
|
-
nil # Evaluate to nil if not found
|
104
|
-
}
|
179
|
+
end
|
105
180
|
|
106
|
-
|
107
|
-
|
181
|
+
# Iterate over each path glob the dir + program.
|
182
|
+
path.split(File::PATH_SEPARATOR).each{ |dir|
|
183
|
+
next unless File.exists?(dir) # In case of bogus second argument
|
184
|
+
file = File.join(dir, program)
|
108
185
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
# that isn't defined, which you may optionally include in +program+.
|
115
|
-
#
|
116
|
-
# Returns nil if not found.
|
117
|
-
#
|
118
|
-
def self.whereis(program, path=ENV['PATH'])
|
119
|
-
dirs = []
|
120
|
-
programs = [program]
|
121
|
-
|
122
|
-
# If no file extension is provided on Windows, try the WIN32EXT's in turn
|
123
|
-
if IS_WINDOWS && File.extname(program).empty?
|
124
|
-
unless WIN32EXTS.include?(File.extname(program).downcase)
|
125
|
-
WIN32EXTS.each{ |ext|
|
126
|
-
programs.push(program + ext)
|
127
|
-
}
|
128
|
-
end
|
186
|
+
# Dir[] doesn't handle backslashes properly, so convert them. Also, if
|
187
|
+
# the program name doesn't have an extension, try them all.
|
188
|
+
if MSWINDOWS
|
189
|
+
file = file.tr("\\", "/")
|
190
|
+
file += WIN32EXTS if File.extname(program).empty?
|
129
191
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
}
|
143
|
-
}
|
144
|
-
dirs.empty? ? nil : dirs.uniq
|
145
|
-
end
|
192
|
+
|
193
|
+
found = Dir[file].first
|
194
|
+
|
195
|
+
# Convert all forward slashes to backslashes if supported
|
196
|
+
if found && File.executable?(found) && !File.directory?(found)
|
197
|
+
found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
|
198
|
+
paths << found
|
199
|
+
end
|
200
|
+
}
|
201
|
+
|
202
|
+
paths.empty? ? nil : paths.uniq
|
203
|
+
end
|
146
204
|
|
147
205
|
# In block form, yields the first +num_lines+ from +filename+. In non-block
|
148
206
|
# form, returns an Array of +num_lines+
|
149
207
|
#
|
208
|
+
# Examples:
|
209
|
+
#
|
210
|
+
# # Return an array
|
211
|
+
# File.head('somefile.txt') # => ['This is line1', 'This is line2', ...]
|
212
|
+
#
|
213
|
+
# # Use a block
|
214
|
+
# File.head('somefile.txt'){ |line| puts line }
|
215
|
+
#
|
150
216
|
def self.head(filename, num_lines=10)
|
151
217
|
a = []
|
152
218
|
IO.foreach(filename){ |line|
|
@@ -172,26 +238,29 @@ class File
|
|
172
238
|
end
|
173
239
|
end
|
174
240
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
241
|
+
# In block form, yields the last +num_lines+ of file +filename+.
|
242
|
+
# In non-block form, it returns the lines as an array.
|
243
|
+
#
|
244
|
+
# Note that this method slurps the entire file, so I don't recommend it
|
245
|
+
# for very large files. Also note that 'tail -f' functionality is not
|
246
|
+
# present. See the 'file-tail' library for that.
|
247
|
+
#
|
248
|
+
# Example:
|
249
|
+
#
|
250
|
+
# File.tail('somefile.txt') # => ['This is line7', 'This is line8', ...]
|
251
|
+
#
|
252
|
+
def self.tail(filename, num_lines=10)
|
253
|
+
if block_given?
|
254
|
+
IO.readlines(filename).reverse[0..num_lines-1].reverse.each{ |line|
|
255
|
+
yield line
|
256
|
+
}
|
257
|
+
else
|
258
|
+
IO.readlines(filename).reverse[0..num_lines-1].reverse
|
259
|
+
end
|
260
|
+
end
|
191
261
|
|
192
262
|
# Converts a text file from one OS platform format to another, ala
|
193
|
-
# 'dos2unix'.
|
194
|
-
# include:
|
263
|
+
# 'dos2unix'. The possible values for +platform+ include:
|
195
264
|
#
|
196
265
|
# * MS Windows -> dos, windows, win32, mswin
|
197
266
|
# * Unix/BSD -> unix, linux, bsd
|
@@ -201,12 +270,12 @@ class File
|
|
201
270
|
# TypeError will be raised. If an invalid format value is received, an
|
202
271
|
# ArgumentError is raised.
|
203
272
|
#
|
204
|
-
def self.nl_convert(
|
205
|
-
unless File.
|
206
|
-
raise
|
273
|
+
def self.nl_convert(old_file, new_file = old_file, platform = 'dos')
|
274
|
+
unless File.file?(old_file)
|
275
|
+
raise ArgumentError, 'Only valid for plain text files'
|
207
276
|
end
|
208
277
|
|
209
|
-
if platform =~ /dos|windows|win32|mswin/i
|
278
|
+
if platform =~ /dos|windows|win32|mswin|cygwin|mingw/i
|
210
279
|
format = "\cM\cJ"
|
211
280
|
elsif platform =~ /unix|linux|bsd/i
|
212
281
|
format = "\cJ"
|
@@ -219,27 +288,28 @@ class File
|
|
219
288
|
orig = $\
|
220
289
|
$\ = format
|
221
290
|
|
222
|
-
if
|
291
|
+
if old_file == new_file
|
223
292
|
require 'fileutils'
|
224
293
|
require 'tempfile'
|
225
294
|
|
226
295
|
begin
|
227
|
-
|
296
|
+
temp_name = Time.new.strftime("%Y%m%d%H%M%S")
|
297
|
+
tf = Tempfile.new('ruby_temp_' + temp_name)
|
228
298
|
tf.open
|
229
299
|
|
230
|
-
IO.foreach(
|
300
|
+
IO.foreach(old_file){ |line|
|
231
301
|
line.chomp!
|
232
302
|
tf.print line
|
233
303
|
}
|
234
304
|
ensure
|
235
305
|
tf.close if tf && !tf.closed?
|
236
306
|
end
|
237
|
-
File.delete(
|
238
|
-
FileUtils.cp(tf.path,
|
307
|
+
File.delete(old_file)
|
308
|
+
FileUtils.cp(tf.path, old_file)
|
239
309
|
else
|
240
310
|
begin
|
241
|
-
nf = File.new(
|
242
|
-
IO.foreach(
|
311
|
+
nf = File.new(new_file, 'w')
|
312
|
+
IO.foreach(old_file){ |line|
|
243
313
|
line.chomp!
|
244
314
|
nf.print line
|
245
315
|
}
|
@@ -252,85 +322,85 @@ class File
|
|
252
322
|
self
|
253
323
|
end
|
254
324
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
325
|
+
# Changes the access and modification time if present, or creates a 0
|
326
|
+
# byte file +filename+ if it doesn't already exist.
|
327
|
+
#
|
328
|
+
def self.touch(filename)
|
329
|
+
if File.exists?(filename)
|
330
|
+
time = Time.now
|
331
|
+
File.utime(time, time, filename)
|
332
|
+
else
|
333
|
+
File.open(filename, 'w'){}
|
334
|
+
end
|
335
|
+
self
|
336
|
+
end
|
267
337
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
338
|
+
# With no arguments, returns a four element array consisting of the number
|
339
|
+
# of bytes, characters, words and lines in filename, respectively.
|
340
|
+
#
|
341
|
+
# Valid options are 'bytes', 'characters' (or just 'chars'), 'words' and
|
342
|
+
# 'lines'.
|
343
|
+
#
|
344
|
+
def self.wc(filename, option='all')
|
345
|
+
option.downcase!
|
346
|
+
valid = %w/all bytes characters chars lines words/
|
277
347
|
|
278
|
-
|
279
|
-
|
280
|
-
|
348
|
+
unless valid.include?(option)
|
349
|
+
raise ArgumentError, "Invalid option: '#{option}'"
|
350
|
+
end
|
281
351
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
352
|
+
n = 0
|
353
|
+
if option == 'lines'
|
354
|
+
IO.foreach(filename){ n += 1 }
|
355
|
+
return n
|
356
|
+
elsif option == 'bytes'
|
357
|
+
File.open(filename){ |f|
|
358
|
+
f.each_byte{ n += 1 }
|
359
|
+
}
|
360
|
+
return n
|
361
|
+
elsif option == 'characters' || option == 'chars'
|
362
|
+
File.open(filename){ |f|
|
363
|
+
while f.getc
|
364
|
+
n += 1
|
365
|
+
end
|
366
|
+
}
|
367
|
+
return n
|
368
|
+
elsif option == 'words'
|
369
|
+
IO.foreach(filename){ |line|
|
370
|
+
n += line.split.length
|
371
|
+
}
|
372
|
+
return n
|
373
|
+
else
|
374
|
+
bytes,chars,lines,words = 0,0,0,0
|
375
|
+
IO.foreach(filename){ |line|
|
376
|
+
lines += 1
|
377
|
+
words += line.split.length
|
378
|
+
chars += line.split('').length
|
379
|
+
}
|
380
|
+
File.open(filename){ |f|
|
381
|
+
while f.getc
|
382
|
+
bytes += 1
|
383
|
+
end
|
384
|
+
}
|
385
|
+
return [bytes,chars,words,lines]
|
386
|
+
end
|
387
|
+
end
|
318
388
|
|
319
|
-
|
389
|
+
private
|
320
390
|
|
321
|
-
|
322
|
-
|
323
|
-
|
391
|
+
def self.bmp?(file)
|
392
|
+
IO.read(file, 3) == "BM6"
|
393
|
+
end
|
324
394
|
|
325
|
-
|
326
|
-
|
327
|
-
|
395
|
+
def self.jpg?(file)
|
396
|
+
IO.read(file, 10) == "\377\330\377\340\000\020JFIF"
|
397
|
+
end
|
328
398
|
|
329
|
-
|
330
|
-
|
331
|
-
|
399
|
+
def self.png?(file)
|
400
|
+
IO.read(file, 4) == "\211PNG"
|
401
|
+
end
|
332
402
|
|
333
|
-
|
334
|
-
|
335
|
-
|
403
|
+
def self.gif?(file)
|
404
|
+
['GIF89a', 'GIF97a'].include?(IO.read(file, 6))
|
405
|
+
end
|
336
406
|
end
|