glob で取得したファイル一覧の順番は保証されるのか

# glob とは？

[Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%AD%E3%83%96)より引用

> グロブ（英: glob）とは主にUnix系環境において、ワイルドカードでファイル名のセットを指定するパターンのことである。

例えば BSD 版 ls コマンドで次のようにワイルドカード `*` を指定すると、マッチしたファイルのパスが出力されます。

```
% ls files/*.txt
files/1.txt     files/4.txt     files/8.txt
files/10.txt    files/5.txt     files/9.txt
files/2.txt     files/6.txt
files/3.txt     files/7.txt
```

Ruby ではこの順番が常に保証されていると勘違いしてハマってしまったので、これを機にいくつかの言語で順番が保証されているか調べてみました。

# 結果

結果は次の通りです。

| 言語 | バージョン | 保証されるか |
| ---- | ------- | --------- |
| Ruby | 2.4.2 | 不明 |
| Perl | 6.18.2 | 指定フラグによる（デフォルト保証） |
| Python | 3.6.3 | 不明 |
| PHP | 7.1.7  | 指定フラグによる（デフォルト保証） |
| Go | 1.9.2 | 不明 |

不明なもので順番の保証を期待する場合は、明示的にソートを行う必要がありそうです。

# 準備

各言語で glob の出力結果を確認するために、次のようにファイルを作成しておきます。

```
% mkdir files
% touch files/{1..10}.txt 
```

以降、各言語でのドキュメントと簡単なサンプルコードによる出力結果を確認します。

# Ruby(2.4.2)

[Dir\.glob](https://docs.ruby-lang.org/ja/latest/method/Dir/s/=5b=5d.html) には言及がありませんでした。

「取得後に明示的にソートする必要がある」というページを見つけましたが、順番を保証しているかどうかは不明でした。

- [Ruby で Dir\.glob して require するときは sort してからにする \| Born Too Late](https://blog.yuyat.jp/post/make-sure-loading-order-to-be-consistent/)
- [ruby \- Does Dir\.glob guarantee the order? \- Stack Overflow](https://stackoverflow.com/questions/6220753/does-dir-glob-guarantee-the-order)

確認コードです。

```rb
Dir.glob("files/*").each{|i| puts i}

# 出力:
#
# files/10.txt
# files/9.txt
# files/8.txt
# files/5.txt
# files/4.txt
# files/6.txt
# files/7.txt
# files/3.txt
# files/2.txt
# files/1.txt
```

# Perl(6.18.2)

`glob` は内部的に`File::Glob` を呼んでいます（[参考](http://perldoc.perl.org/functions/glob.html)）。

[File::Glob](http://perldoc.perl.org/File/Glob.html) は POSIX フラグを指定可能で、これによって表示順の保証が変わります。

| フラグ | 保証されるか |
| ----- | --------- |
| `GLOB_NOSORT` | されない |
| `GLOB_ALPHASORT` | される |
| 指定なし | される |

まず、 `GLOB_NOSORT` が指定されているとソートは行われません（順番は**保証されない**）。

次に `GLOB_ALPHASORT` が指定されていると、大文字小文字を区別しないアルファベット順のソートが行われます（順番は**保証される**）。

また、フラグが未指定の場合は `GLOB_CSH` が設定されます。`GLOB_CSH` は`GLOB_BRACE | GLOB_NOMAGIC | GLOB_QUOTE | GLOB_TILDE | GLOB_ALPHASORT` の指定になるので、結果的に大文字小文字を区別しないアルファベット順のソートが行われます（順番は**保証される**）。

確認コードです。

```pl
my @all_files = glob "files/*";
print join("\n", @all_files) . "\n\n";

# 出力:
#
# files/1.txt
# files/10.txt
# files/2.txt
# files/3.txt
# files/4.txt
# files/5.txt
# files/6.txt
# files/7.txt
# files/8.txt
# files/9.txt
# 
```

ちなみに glob には POSIX フラグを指定できないので、次のように直接 `bsd_glob` を呼び、そのパラメータとしてフラグを指定します。

```pl
use File::Glob ':bsd_glob';
my @all_files = bsd_glob("files/*", GLOB_NOSORT);
print join("\n", @all_files) . "\n\n";

# 出力:
# files/10.txt
# files/9.txt
# files/8.txt
# files/5.txt
# files/4.txt
# files/6.txt
# files/7.txt
# files/3.txt
# files/2.txt
# files/1.txt
#
```

# Python(3.6.3)

[glob](https://docs.python.jp/3/library/glob.html#glob.glob) には言及がありませんでした。

Perl 同様、いくつかのページで「取得後に明示的にソートする必要がある」という内容はありましたが、保証しているかどうかは不明でした。

- [How is Pythons glob\.glob ordered? \- Stack Overflow](https://stackoverflow.com/questions/6773584/how-is-pythons-glob-glob-ordered)
- [\[Tutor\] glob in order of the file numbers](https://mail.python.org/pipermail/tutor/2009-March/067742.html)


確認コードです。

```py
import glob

for i in glob.glob("files/*"):
    print(i)

# 出力:
#
# files/10.txt
# files/9.txt
# files/8.txt
# files/5.txt
# files/4.txt
# files/6.txt
# files/7.txt
# files/3.txt
# files/2.txt
# files/1.txt
```

# PHP(7.1.7)

指定するフラグによって表示順の保証が変わります。

| フラグ | 保証されるか |
| ----- | --------- |
| `GLOB_NOSORT` | されない |
| 指定なし | される |

確認コードです。

```php
<?php
foreach (glob("files/*") as $i) {
    echo "$i\n";
}
?>

/**
 * 出力:
 * 
 * files/1.txt
 * files/10.txt
 * files/2.txt
 * files/3.txt
 * files/4.txt
 * files/5.txt
 * files/6.txt
 * files/7.txt
 * files/8.txt
 * files/9.txt
 */
```

`GLOB_NOSORT` を指定したコードです。

```php
<?php
foreach (glob("files/*", GLOB_NOSORT) as $i) {
    echo "$i\n";
}
?>

/**
 * 出力:
 * 
 * files/10.txt
 * files/9.txt
 * files/8.txt
 * files/5.txt
 * files/4.txt
 * files/6.txt
 * files/7.txt
 * files/3.txt
 * files/2.txt
 * files/1.txt
 */
```

参考

- [PHP: glob \- Manual](http://php.net/manual/ja/function.glob.php)

# Go(1.9.2)

[filepath \- GoDoc](https://godoc.org/path/filepath#Glob) には言及がありませんでした。

関連した話題のページはありましたが、Ruby/Python と同様不明でした。

- [path/filepath: ensure Glob\(\) returns in lexical order and comment about that · Issue \#17153 · golang/go](https://github.com/golang/go/issues/17153)
- [\[go\-nuts\] Order of files returned by filepath\.Glob \- Grokbase](http://grokbase.com/t/gg/golang-nuts/1418jwsxp1/go-nuts-order-of-files-returned-by-filepath-glob)

```go
package main

import (
        "fmt"
        "path/filepath"
)

func main() {
        files, err := filepath.Glob("files/*")
        if err != nil {
                panic(err)
        }
        for _, file := range files {
                fmt.Println(file)
        }
}

/**
 * 出力結果:
 * 
 * files/1.txt
 * files/10.txt
 * files/2.txt
 * files/3.txt
 * files/4.txt
 * files/5.txt
 * files/6.txt
 * files/7.txt
 * files/8.txt
 * files/9.txt
 */
```