Files and Directories

The File class defines quite a few class methods for working with files as entries in a filesystem: methods for testing the size or existence of a named file, for example, and methods for separating a filename from the directory name that precedes it. These are class methods and they do not operate on File objects; instead, filenames are specified as strings. Similarly, the Dir class defines class methods for working with and reading filenames from filesystem directories. The subsections that follow demonstrate how to:

  • Work with and manipulate filenames and directory names

  • List directories

  • Test files to determine their type, size, modification time, and other attributes

  • Delete, rename, and perform similar operations on files and directories

Note that the methods described here query and manipulate files, but do not read or write file content. Reading and writing files is covered in Input/Output.

File and Directory Names

The class methods of the File and Dir classes operate on files and directories specified by name. Ruby uses Unix-style filenames with / as the directory separator character. You can use the forward slash character in your filenames, even when using Ruby on a Windows platform. On Windows, Ruby can also handle filenames that use the backslash character and that include drive letter prefixes. The constant File::SEPARATOR should be '/' in all implementations. File::ALT_SEPARATOR is '' on Windows, and is nil on other platforms.

The File class defines some methods for manipulating filenames:

full = '/home/matz/bin/ruby.exe'
file=File.basename(full)     # => 'ruby.exe': just the local filename
File.basename(full, '.exe')  # => 'ruby': with extension stripped
dir=File.dirname(full)       # => '/home/matz/bin': no / at end
File.dirname(file)           # => '.': current directory
File.split(full)             # => ['/home/matz/bin', 'ruby.exe']
File.extname(full)           # => '.exe'
File.extname(file)           # => '.exe'
File.extname(dir)            # => ''
File.join('home','matz')     # => 'home/matz': relative
File.join('','home','matz')  # => '/home/matz': absolute

The File.expand_path method converts a relative path to a fully qualified path. If the optional second argument is supplied, it is first prepended as a directory to the first argument. The result is then converted to an absolute path. If it begins with a Unix-style ~, the directory is relative to the current user or specified user’s home directory. Otherwise, the directory is resolved relative to the current working directory (see Dir.chdir below to change the working directory):

Dir.chdir("/usr/bin")      # Current working directory is "/usr/bin"
File.expand_path("ruby")       # => "/usr/bin/ruby"
File.expand_path("~/ruby")     # => "/home/david/ruby"
File.expand_path("~matz/ruby") # => "/home/matz/ruby"
File.expand_path("ruby", "/usr/local/bin") # => "/usr/local/bin/ruby"
File.expand_path("ruby", "../local/bin")   # => "/usr/local/bin/ruby"
File.expand_path("ruby", "~/bin")          # => "/home/david/bin/ruby"

The File.identical? method tests whether two filenames refer to the same file. This might be because the names are the same, but it is a more useful method when the names differ. Two different names might refer to the same file if one is a relative filename and the other is absolute, for example. One might include “..” to go up a level and then down again. Or, two different names might refer to the same file if one name is a symbolic link or shortcut (or hard link on platforms that support it) to the other. Note, however, that File.identical? only returns true if the two names refer to the same file and that file actually exists. Also note that File.identical? does not expand the ~ character for home directories the way that File.expand_path does:

File.identical?("ruby", "ruby")          # => true if the file exists
File.identical?("ruby", "/usr/bin/ruby") # => true if CWD is /usr/bin
File.identical?("ruby", "../bin/ruby")   # => true if CWD is /usr/bin
File.identical?("ruby", "ruby1.9")       # => true if there is a link

Finally, File.fnmatch tests whether a filename matches a specified pattern. The pattern is not a regular expression, but is like the file-matching patterns used in shells.?” matches a single character.*” matches any number of characters. And “**” matches any number of directory levels. Characters in square brackets are alternatives, as in regular expressions. fnmatch does not allow alternatives in curly braces (as the Dir.glob method described below does). fnmatch should usually be invoked with a third argument of File::FNM_PATHNAME, which prevents “*” from matching “/”. Add File::FNM_DOTMATCH if you want “hidden” files and directories whose names begin with “.” to match. Only a few examples of fnmatch are given here. Use ri File.fnmatch for complete details. Note that File.fnmatch? is a synonym:

File.fnmatch("*.rb", "hello.rb")     # => true
File.fnmatch("*.[ch]", "ruby.c")     # => true
File.fnmatch("*.[ch]", "ruby.h")     # => true
File.fnmatch("?.txt", "ab.txt")      # => false
flags = File::FNM_PATHNAME | File::FNM_DOTMATCH
File.fnmatch("lib/*.rb", "lib/a.rb", flags)      # => true
File.fnmatch("lib/*.rb", "lib/a/b.rb", flags)    # => false
File.fnmatch("lib/**/*.rb", "lib/a.rb", flags)   # => true
File.fnmatch("lib/**/*.rb", "lib/a/b.rb", flags) # => true

Listing Directories

The easiest way to list the contents of a directory is with the Dir.entries method or the Dir.foreach iterator:

# Get the names of all files in the config/ directory 
filenames = Dir.entries("config")        # Get names as an array
Dir.foreach("config") {|filename| ... }  # Iterate names

The names returned by these methods are not guaranteed to be in any particular order, and (on Unix-like platforms) include “.” (the current directory) and “..” (the parent directory). To obtain a list of files that match a given pattern, use the Dir.[] operator:

Dir['*.data']       # Files with the "data" extension
Dir['ruby.*']       # Any filename beginning with "ruby."
Dir['?']            # Any single-character filename
Dir['*.[ch]']       # Any file that ends with .c or .h
Dir['*.{java,rb}']  # Any file that ends with .java or .rb
Dir['*/*.rb']       # Any Ruby program in any direct sub-directory
Dir['**/*.rb']      # Any Ruby program in any descendant directory

A more powerful alternative to Dir[] is Dir.glob. (The verb “glob” is an old Unix term for filename matching in a shell.) By default, this method works like Dir[], but if passed a block, it yields the matching filenames one at a time rather than returning an array. Also, the glob method accepts an optional second argument. If you pass the constant File::FNM_DOTMATCH (see File.fnmatch previously) as this second argument, then the result will include files whose names begin with “.” (on Unix systems, these files are hidden and are not returned by default):

Dir.glob('*.rb') {|f| ... }      # Iterate all Ruby files
Dir.glob('*')                    # Does not include names beginning with '.'
Dir.glob('*',File::FNM_DOTMATCH) # Include . files, just like Dir.entries

The directory listing methods shown here, and all File and Dir methods that resolve relative pathnames, do so relative to the “current working directory,” which is a value global to the Ruby interpreter process. Query and set the CWD with the getwd and chdir methods:

puts Dir.getwd          # Print current working directory
Dir.chdir("..")         # Change CWD to the parent directory
Dir.chdir("../sibling") # Change again to a sibling directory
Dir.chdir("/home")      # Change to an absolute directory
Dir.chdir               # Change to user's home directory
home = Dir.pwd          # pwd is an alias for getwd

If you pass a block to the chdir method, the directory will be restored to its original value when the block exits. Note, however, that while the directory change is limited in duration, it is still global in scope and affects other threads. Two threads may not call Dir.chdir with a block at the same time.

Testing Files

File defines a slew of methods to obtain metadata about a named file or directory. Many of the methods return low-level information that is OS-dependent. Only the most portable and broadly useful are demonstrated here. Use ri on the File and File::Statclasses for a complete list of methods.

The following simple methods return basic information about a file. Most are predicates that return true or false:

f = "/usr/bin/ruby"      # A filename for the examples below

# File existence and types.
File.exist?(f)           # Does the named file exist? Also: File.exists?
File.file?(f)            # Is it an existing file?
File.directory?(f)       # Or is it an existing directory?
File.symlink?(f)         # Either way, is it a symbolic link?

# File size methods. Use File.truncate to set file size.
File.size(f)             # File size in bytes.
File.size?(f)            # Size in bytes or nil if empty file.
File.zero?(f)            # True if file is empty.

# File permissions. Use File.chmod to set permissions (system dependent).
File.readable?(f)        # Can we read the file?
File.writable?(f)        # Can we write the file? No "e" in "writable"
File.executable?(f)      # Can we execute the file?
File.world_readable?(f)  # Can everybody read it? Ruby 1.9.
File.world_writable?(f)  # Can everybody write it? Ruby 1.9.

# File times/dates. Use File.utime to set the times.
File.mtime(f)            # => Last modification time as a Time object
File.atime(f)            # => Last access time as a Time object

Another way to determine the type (file, directory, symbolic link, etc.) of a filename is with ftype, which returns a string that names the type. Assume that /usr/bin/ruby is a symbolic link (or shortcut) to /usr/bin/ruby1.9:

File.ftype("/usr/bin/ruby")    # => "link"
File.ftype("/usr/bin/ruby1.9") # => "file"
File.ftype("/usr/lib/ruby")    # => "directory"
File.ftype("/usr/bin/ruby3.0") # SystemCallError: No such file or directory

If you are interested in multiple pieces of information about a file, it may be more efficient to call stat or lstat. (stat follows symbolic links; lstat returns information about the link itself.) These methods return a File::Stat object, which has instance methods with the same names (but without arguments) as the class methods of File. The efficiency of using stat is that Ruby only has to make one call to the OS to obtain all file metadata. Your Ruby program can then obtain that information from the File::Stat object as it needs it:

s = File.stat("/usr/bin/ruby")
s.file?             # => true
s.directory?        # => false
s.ftype             # => "file"
s.readable?         # => true
s.writable?         # => false
s.executable?       # => true
s.size              # => 5492
s.atime             # => Mon Jul 23 13:20:37 -0700 2007

Use ri on File::Stat for a full list of its methods. One final general-purpose file testing method is Kernel.test. It exists for historical compatibility with the Unix shell command test. The test method is largely obviated by the class methods of the File class, but you may see it used in existing Ruby scripts. Use ri for complete details:

# Testing single files
test ?e, "/usr/bin/ruby"   # File.exist?("/usr/bin/ruby")
test ?f, "/usr/bin/ruby"   # File.file?("/usr/bin/ruby")
test ?d, "/usr/bin/ruby"   # File.directory?("/usr/bin/ruby")
test ?r, "/usr/bin/ruby"   # File.readable?("/usr/bin/ruby")
test ?w, "/usr/bin/ruby"   # File.writeable?("/usr/bin/ruby")
test ?M, "/usr/bin/ruby"   # File.mtime("/usr/bin/ruby")
test ?s, "/usr/bin/ruby"   # File.size?("/usr/bin/ruby")

# Comparing two files f and g
test ?-, f, g              # File.identical(f,g)
test ?<, f, g              # File(f).mtime < File(g).mtime
test ?>, f, g              # File(f).mtime > File(g).mtime
test ?=, f, g              # File(f).mtime == File(g).mtime

Creating, Deleting, and Renaming Files and Directories

The File class does not define any special methods for creating a file. To create one, simply open it for writing, write zero or more bytes, and close it again. If you don’t want to clobber an existing file, open it in append mode:

# Create (or overwrite) a file named "test"
File.open("test", "w") {}
# Create (but do not clobber) a file named "test"
File.open("test", "a") {}

To copy a file, use File.copy_stream, specifying filenames as the source and destination:

File.copy_stream("test", "test.backup")

To change the name of a file, use File.rename:

File.rename("test", "test.old")     # Current name, then new name

To create a symbolic link to a file, use File.symlink:

File.symlink("test.old", "oldtest") # Link target, link name

On systems that support it, you can create a “hard link” with File.link:

File.link("test.old", "test2")   # Link target, link name

Finally, to delete a file or link, use File.delete, or its synonym File.unlink:

File.delete("test2")   # May also be called with multiple args
File.unlink("oldtest") # to delete multiple named files

On systems that support it, use File.truncate to truncate a file to a specified number (possibly zero) of bytes. Use File.utime to set the access and modification times of a file. And use the platform-dependent method File.chmod to change the permissions of a file:

f = "log.messages"          # Filename
atime = mtime = Time.now    # New access and modify times
File.truncate(f, 0)         # Erase all existing content
File.utime(atime, mtime, f) # Change times
File.chmod(0600, f)         # Unix permissions -rw-------; note octal arg

To create a new directory, use Dir.mkdir. To delete a directory, use Dir.rmdir or one of its synonyms, Dir.delete or Dir.unlink. The directory must be empty before it can be deleted:

Dir.mkdir("temp")                 # Create a directory
File.open("temp/f", "w") {}       # Create a file in it
File.open("temp/g", "w") {}       # Create another one
File.delete(*Dir["temp/*"])       # Delete all files in the directory
Dir.rmdir("temp")                 # Delete the directory
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.83.199