こんにちは! フリーエンジニアの長瀬です。
みなさんはディレクトリ(フォルダ)やファイルをプログラムで管理していますか?
ディレクトリやファイルの操作を極めれば、繰り返し大量のファイルを作成できたりととても便利です。
この記事では、ディレクトリやファイル操作について
・ ディレクトリの作成
・ 現在のディレクトリの取得
・ ディレクトリの削除
・ ディレクトリのコピー
・ ディレクトリの移動
・ ディレクトリの名前変更
・ ディレクトリの名前を取り出す
・ カレントディレクトリの移動
・ ディレクトリが存在するか確認
・ ディレクトリのファイル一覧を取得
という基本的な内容から、
・ ホームディレクトリを取得
・ ディレクトリかどうか判断する
・ ファイル名をディレクトリの部分だけにする
・ ディレクトリ内のファイル全てに処理をする
・ 任意の文字列を含むファイルを取得
・ findでファイルを取得する
・ ファイルを再帰的に作成する
といった応用的な内容についても解説していきます。
ディレクトリ操作の基本
ディレクトリの作成
Dir::mkdir("sample")
ファイル名の部分は現在のRubyファイルから見た相対パスを入力しましょう。
相対パスとは、Rubyファイルから見たそのディレクトリの位置を指します。
同じ階層であれば「./」、一つ上のディレクトリであれば「../」とします。
同じ階層にファイルを作りたければ、特に気にせずにファイル名を入力してください。
またディレクトリを作成する際は現在のディレクトリを確認しましょう。
現在のディレクトリの取得
「pwd」は「Print Working Directory」のことで現在作業をしているディレクトリを取得します。
基本的なコマンドで、よく使うので2秒で入力できるようにしておきましょう。
Dir::pwd
ディレクトリの削除
ディレクトリを削除するには3つのコマンドがあります。
Dir::rmdir("sample") Dir::delete("sample") Dir::unlink("sample")
これで削除できるのですが、中身が空である時のみ削除が実行されます。
中身が空でなければ削除できないので気を付けましょう。
ディレクトリのコピー
ディレクトリのコピーは「FileUtils」を用います。
require 'fileutils' FileUtils.cp_r("sample","sample2")
サンプルのソースコードは次の通りですが、このサンプルはあらかじめ「tmp」というフォルダに「a.txt」を入れてあります。
require "fileutils" FileUtils.cp_r("tmp/","tmp2/")
「tmp2」というディレクトリが無くてもそれを生成して中身をコピーしてくれます。
また、「tmp2」というディレクトリをあらかじめ作成しておくと「tmp2」内部に「tmp」を丸ごとコピーしてくれます。
FileUtilesの使い方はこちらで詳しく説明してありますので、一読することをおすすめします。
ディレクトリの移動
require 'fileutils' FileUtils.mv("sample","sample2")
このプログラムを実行すると、ディレクトリsampleがsample2に変更されます。
ディレクトリの名前変更
File.rename("sample","sample2")
これでsample→sample2に変わります。
ディレクトリの名前を取り出す
ディレクトリの名前を取得します。
File.dirname("sample")
指定したファイルの存在するカレントディレクトリを取得することができます。
使い方は以下の通りですが、事前に「a.txt」というフォルダを作成しておきましょう。
puts File.dirname("a.txt")
実行結果は次のようになります。
.
「.」はカレントディレクトリを表します。
カレントディレクトリとは現在のディレクトリのことです。
「.」は現在のディレクトリを表現したいときによく使用します。
カレントディレクトリの移動
今いるディレクトリを移動するときに使います。
Dir::chdir("sample")
使い方は次の通りです。
1つ上のディレクトリに移動して、現在地を表示しています。
Dir::chdir("..") puts Dir::pwd
ディレクトリが存在するか確認
Dir::exist?("sample")
True か False で表示されます。
ディレクトリのファイル一覧を取得
Dir::entries("sample")
以下のように記述するとカレントディレクトリに存在するファイルを取得します。
Dir::entries(".")
ディレクトリ操作の応用
ホームディレクトリを取得
ホームディレクトリに相当するディレクトリを取得できます。
Dir::home
ディレクトリかどうか判断する
File::ftype("sample")
ディレクトリである場合にtrueを返します。
反対にディレクトリでない場合にはfalseを返します。
ファイル名をディレクトリの部分だけにする
p File::dirname("/usr/local/bin/ruby/sample") p File::dirname("/etc/sample/main")
/usr/local/bin/ruby
/etc/sample
がそれぞれ取り出されます。
ディレクトリ内のファイル全てに処理をする
Dir::foreach("sample"){ 処理 }
それか
Dir::open("sample"){|d| d.each{ 処理 }
です。
以下にサンプルを書きます。
あらかじめ、「a.txt」「b.txt」「c.txt」を作成しました。
Dir::foreach("."){|f| puts f }
Dir::open('.') {|d| d.each {|f| puts f } }
「a.txt」「b.txt」「c.txt」は出力されましたでしょうか。
このように1つ1つに処理を適用することもできます。
任意の文字列を含むファイルを取得
ワイルドカードと呼ばれるパターン認識を使えば、あるパターンに合致したファイルだけを取得することができます。
p Dir.glob("*")
以下にサンプルを書きます。
例えば、ディレクトリ下にfire.rb、water.rb、thunder.rbがあるします。
この中からfire.rbだけを取得したい場合は
p Dir.glob("f*")
と書けます。
*は”なんでも”表現しているので、例えばこの場合fire.rb以外にfjeoiwjrjei.rbやf458773747lfj.txtがあったとしても取得されます。
また、複数の条件を指定する場合は
p Dir.glob(["f*","t*"])
と書けて、この場合fire.rbとthunder.rbが取得されます。
findでファイルを取得する
findメソッドを使っても、ファイルを取得することができます。
先ほど紹介したglobとの違いはファイルの読み込み方法にあります。
globは始めにすべてファイルを取得してから処理を実行しますが、findメソッドでは1つ1つファイルに処理を加えていきます。
findの優位な点はglobに比べて処理を開始する時間が若干速いということです。
というのも、globは始めにすべてのファイルを読み込む分だけ遅くなるからです。
findで現在のディレクトリのファイル一覧を取得するには以下のようにします。
require 'find' Find.find('.') do |file| next if file == '.' puts file end
「.」は現在のディレクトリを表し、出力結果にも「.」が含まれます。
なので、nextを使って、’.’のときは出力しないように工夫しています。
nextを使った後置ifについてはこちらで体系的にまとめられているのでよくわからないという方は一読することをおすすめします
ファイルを再帰的に作成する
これまで、学習した知識を組みわせて再帰的にファイルを作成してみましょう。
以下のコードは現在のディレクトリにhoge0、hoge1、hoge2という新たなディレクトリを作成して、それぞれに0.txt、1.txt、2.txtというファイルを再帰的に作成します。
3.times do |i| directory = "hoge#{i}" Dir::mkdir(directory) Dir::chdir(directory) 3.times do |j| f = File.new("#{j}.txt",'w') f.close end Dir::chdir('..') end
実行すると計9つのファイルが作成されているのを確認できます。
また、上記のコードでは本記事で学習したことのほかに、Fileクラスを使って動的にファイルを作成していますが、Fileクラスについて理解が浅いと思う方はこちらの記事を一読することをおすすめします。
まとめ
いかがだったでしょうか。
ディレクトリの操作はDir, File, FileUtilsの三つのクラスで行うという事が分かっていただけたでしょうか。
Railsなどのgemの内部コードでもこれらのディレクトリ操作はよく出てくるため、覚えておいて損はないでしょう。
サーバーなどでもRubyを用いてディレクトリ操作ができるとできることも広がります。
ぜひ、習得してみましょう!