Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11986] ファイルの概要

このコミットは、Go言語の標準ライブラリであるpath/filepathパッケージのfilepath.Walk関数の使用例を、Go 1の公式ドキュメントに追加するものです。具体的には、filepath.Walkの動作を説明するHTMLドキュメント(doc/go1.htmldoc/go1.tmpl)に、新しいGoプログラムの例(doc/progs/go1.go)を埋め込むことで、ユーザーがfilepath.Walkの挙動、特にディレクトリのスキップ(filepath.SkipDir)の方法を視覚的に理解できるように改善しています。

コミット

  • コミットハッシュ: 91672686da0af0d2e21b022c36b9977a78ec490f
  • 作者: Mike Rosset mike.rosset@gmail.com
  • 日付: 2012年2月17日金曜日 12:45:55 +1100
  • コミットメッセージ:
    doc: provide example filepath.Walk for go1
    
    R=golang-dev, r, r
    CC=golang-dev
    https://golang.org/cl/5674067
    

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/91672686da0af0d2e21b022c36b9977a78ec490f

元コミット内容

doc: provide example filepath.Walk for go1

R=golang-dev, r, r
CC=golang-dev
https://golang.org/cl/5674067

変更の背景

Go言語の初期バージョンであるGo 1のリリースに向けて、ドキュメントの充実が図られていました。path/filepathパッケージのWalk関数は、ファイルシステムを再帰的に走査するための非常に強力なツールですが、その使用方法、特に特定のディレクトリをスキップするfilepath.SkipDirの挙動は、初心者には直感的ではない場合があります。

このコミットの背景には、filepath.Walkのドキュメントに具体的なコード例が不足しており、ユーザーがこの関数を効果的に利用するための理解を深める必要があったという課題があります。既存のドキュメントには「TODO: add an example?」というコメントがあり、例の追加が求められていたことが示されています。この変更は、その要望に応え、Go 1のドキュメントの品質とユーザーフレンドリーさを向上させることを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念と標準ライブラリに関する知識が必要です。

  1. path/filepathパッケージ:

    • Go言語でファイルパスを操作するためのユーティリティを提供するパッケージです。プラットフォーム固有のパス区切り文字(Windowsの\、Unix系の/)を抽象化し、クロスプラットフォームなパス操作を可能にします。
    • filepath.Walk(root string, walkFn WalkFunc) error: 指定されたrootパスから開始して、ファイルツリーを再帰的に走査する関数です。walkFnというコールバック関数を、走査中に見つかった各ファイルやディレクトリに対して呼び出します。
    • WalkFunc: filepath.Walkに渡されるコールバック関数の型定義です。
      type WalkFunc func(path string, info os.FileInfo, err error) error
      
      • path: 現在走査しているファイルまたはディレクトリのパス。
      • info: そのファイルまたはディレクトリのos.FileInfoインターフェース。ファイル名、サイズ、パーミッション、変更時刻などの情報を提供します。
      • err: filepath.Walkpathにアクセスしようとした際に発生したエラー。エラーがない場合はnilWalkFuncnil以外のエラーを返すと、filepath.Walkはそのエラーを返して走査を停止します。
    • filepath.SkipDir: WalkFuncがこの特殊なエラー値を返すと、filepath.Walkは現在のディレクトリの内容をスキップし、そのディレクトリの兄弟要素の走査を続行します。これは、特定のサブディレクトリを処理対象から除外したい場合に非常に便利です。
  2. osパッケージ:

    • オペレーティングシステム機能へのアクセスを提供するパッケージです。
    • os.FileInfoインターフェース: ファイルやディレクトリのメタデータ(名前、サイズ、モード、変更時刻など)を抽象的に表現するためのインターフェースです。filepath.WalkWalkFuncに渡されるinfo引数として使用されます。
  3. logパッケージ:

    • ログメッセージを出力するためのシンプルなパッケージです。
    • log.Println(v ...interface{}): 引数をデフォルトのフォーマットで標準エラー出力に書き込み、改行を追加します。
    • log.Fatal(v ...interface{}): Printlnと同様にログを出力した後、os.Exit(1)を呼び出してプログラムを終了します。
  4. Goのドキュメント生成:

    • Goのドキュメントは、Goのソースコード内のコメントや、専用のMarkdown/HTMLファイルから生成されます。このコミットでは、doc/go1.htmldoc/go1.tmplがGo 1のドキュメントの一部であり、doc/progs/go1.goはドキュメントに埋め込まれるコード例を格納するファイルです。
    • {{code "progs/go1.go" /STARTWALK/ /ENDWALK/}}のような構文は、Goのドキュメントツールがprogs/go1.goファイルから/STARTWALK//ENDWALK/マーカーで囲まれたコードブロックを抽出し、それをHTMLドキュメントに埋め込むためのディレクティブです。これにより、ドキュメント内のコード例と実際の実行可能なコードが同期され、常に最新の状態に保たれます。

技術的詳細

このコミットは、Go 1のドキュメントにおけるfilepath.Walk関数の説明セクションに、具体的なコード例を組み込むことで、その理解を深めることを目的としています。

変更は主に以下の3つのファイルにわたります。

  1. doc/go1.html:

    • このファイルは、Go 1のドキュメントのHTMLバージョンです。
    • 以前はfilepath.Walkの説明の後に「TODO: add an example?」というコメントと、赤字で例の追加を促すプレースホルダーがありました。
    • このコミットでは、そのプレースホルダーが削除され、代わりにdoc/progs/go1.goから抽出されたfilepath.Walkの具体的なGoコード例が<pre>タグ内に直接埋め込まれています。
    • また、SkipDirの参照が単なるコードスニペットから、/pkg/path/filepath/#variablesへのハイパーリンクを含むfilepath.SkipDirに変更され、より公式なドキュメントへの参照が提供されています。
  2. doc/go1.tmpl:

    • このファイルは、Go 1のドキュメントのテンプレートファイルであり、HTMLドキュメントを生成するための元となるものです。
    • doc/go1.htmlと同様に、「TODO: add an example?」のコメントとプレースホルダーが削除されました。
    • 最も重要な変更は、{{code "progs/go1.go" /STARTWALK/ /ENDWALK/}}というテンプレートディレクティブが追加されたことです。これは、Goのドキュメント生成システムに対し、doc/progs/go1.goファイル内のSTARTWALKENDWALKというコメントマーカーで囲まれたコードブロックを抽出し、この位置に挿入するように指示します。これにより、ドキュメントのHTMLバージョンとテンプレートバージョンの両方で、動的にコード例が埋め込まれるようになります。
    • SkipDirの参照もfilepath.SkipDirに更新され、パッケージ内の変数へのリンクが追加されています。
  3. doc/progs/go1.go:

    • このファイルは、Go 1のドキュメント内で使用される様々なコード例をまとめたGoプログラムです。
    • 新しいfunc walkExample()関数が追加されました。この関数はfilepath.Walkの具体的な使用例を含んでいます。
    • main関数内でwalkExample()が呼び出されるように変更され、この例が実行可能なプログラムの一部として統合されました。
    • path/filepathパッケージがインポートリストに追加されました。
    • // STARTWALK OMIT// ENDWALK OMITという特殊なコメントがwalkExample関数の前後に追加されています。これらは、doc/go1.tmplで使用されている{{code ...}}ディレクティブが、この特定のコードブロックを正確に抽出するために使用するマーカーです。OMITは、ドキュメント生成時にこれらのコメント行自体は出力しないことを示します。

これらの変更により、Go 1のドキュメントはfilepath.Walkの機能とfilepath.SkipDirの利用方法を、実際の動作するコード例を通じてより明確に説明できるようになりました。

コアとなるコードの変更箇所

このコミットのコアとなるコードの変更は、主にdoc/progs/go1.goファイルに新しいwalkExample関数が追加された点です。

--- a/doc/progs/go1.go
+++ b/doc/progs/go1.go
@@ -12,6 +12,7 @@ import (
 	"fmt"
 	"log"
 	"os"
+	"path/filepath"
 	"testing"
 	"time"
 	"unicode"
@@ -28,6 +29,7 @@ func main() {
 	runType()
 	errorExample()
 	timePackage()
+	walkExample()
 	osIsExist()
 }

@@ -183,6 +185,25 @@ func timePackage() {
 	sleepUntil(time.Now().Add(123 * time.Millisecond))\n}\n
+func walkExample() {
+	// STARTWALK OMIT
+	markFn := func(path string, info os.FileInfo, err error) error {
+		if path == "pictures" { // Will skip walking of directory pictures and its contents.
+			return filepath.SkipDir
+		}
+		if err != nil {
+			return err
+		}
+		log.Println(path)
+		return nil
+	}
+	err := filepath.Walk(".", markFn)
+	if err != nil {
+		log.Fatal(err)
+	}
+	// ENDWALK OMIT
+}
+
 func initializationFunction(c chan int) {
 	c <- 1
 }

コアとなるコードの解説

追加されたwalkExample関数は、filepath.Walkの典型的な使用パターンを示しています。

func walkExample() {
	// STARTWALK OMIT
	markFn := func(path string, info os.FileInfo, err error) error {
		if path == "pictures" { // Will skip walking of directory pictures and its contents.
			return filepath.SkipDir
		}
		if err != nil {
			return err
		}
		log.Println(path)
		return nil
	}
	err := filepath.Walk(".", markFn)
	if err != nil {
		log.Fatal(err)
	}
	// ENDWALK OMIT
}
  1. markFnの定義:

    • filepath.Walkに渡すWalkFunc型の匿名関数markFnを定義しています。
    • この関数は、path(現在のファイル/ディレクトリのパス)、info(ファイル情報)、err(エラー)の3つの引数を受け取ります。
  2. ディレクトリのスキップ条件:

    • if path == "pictures" { return filepath.SkipDir }
      • これがこの例の最も重要な部分です。もし現在のパスが文字列 "pictures"と一致した場合(つまり、カレントディレクトリ直下のpicturesという名前のディレクトリに遭遇した場合)、markFnfilepath.SkipDirを返します。
      • filepath.SkipDirが返されると、filepath.Walkpicturesディレクトリとその中のすべての内容の走査をスキップし、picturesディレクトリの兄弟要素(同じ階層にある他のファイルやディレクトリ)の走査を続行します。これにより、特定のサブツリーを効率的に無視できます。
  3. エラーハンドリング:

    • if err != nil { return err }
      • filepath.Walkpathにアクセスする際にエラーが発生した場合(例: 権限がない、ファイルが存在しないなど)、そのエラーをmarkFnに渡します。
      • markFnがこのエラーをそのまま返すと、filepath.Walkは直ちに走査を中止し、そのエラーを呼び出し元に返します。これは、ファイルシステム走査中に致命的な問題が発生した場合の標準的なエラー処理パターンです。
  4. パスの出力:

    • log.Println(path)
      • エラーがなく、かつディレクトリがスキップされない場合、現在のファイルまたはディレクトリのパスを標準出力にログとして出力します。これにより、filepath.Walkが実際にどのパスを訪れたかを確認できます。
  5. 正常終了:

    • return nil
      • markFnnilを返すと、filepath.Walkは走査を続行します。
  6. filepath.Walkの呼び出し:

    • err := filepath.Walk(".", markFn)
      • カレントディレクトリ(.)からファイルシステム走査を開始し、定義したmarkFnをコールバックとして使用します。
  7. メインのエラーハンドリング:

    • if err != nil { log.Fatal(err) }
      • filepath.Walk関数自体がエラーを返した場合(例: markFnがエラーを返して走査が中止された場合)、そのエラーをlog.Fatalで出力し、プログラムを終了します。

この例は、filepath.Walkの基本的な使い方、WalkFuncの構造、そして特にfilepath.SkipDirを使って特定のサブディレクトリを効率的にスキップする方法を明確に示しています。

関連リンク

参考にした情報源リンク

  • 特になし。コミット内容とGo言語の標準ライブラリの知識に基づいて解説を生成しました。