Bash のパターン拡張機能

導入

ユーザーが入力したコマンドを受信した後、シェルはユーザーの入力をスペースに基づいてトークンに分割します。次に、シェルは単語要素内の特殊文字を展開し、展開の完了後に対応するコマンドが呼び出されます。

この特殊文字の拡張は、パターン拡張 (グロビング) と呼ばれます。それらの中には、ワイルドカード拡張とも呼ばれるワイルドカード文字を使用するものもあります。 Bash は合計 8 つの拡張機能を提供します。

  • チルダ拡張子
  • ? 文字の拡張
  • *文字拡張
  • 角括弧の展開
  • ブレースの拡張
  • 変数の展開
  • サブコマンドの拡張
  • 算術拡張

この章では、これら 8 つの拡張機能を紹介します。

Bash は最初に展開してからコマンドを実行します。したがって、拡張の結果は、実行されるコマンドとは無関係に、Bash の責任になります。コマンド自体にはパラメータの展開はなく、受け取ったパラメータはそのまま実行されます。これは覚えておくことが重要です。

モジュール拡張を表す英語は「globbing」です。この単語は、拡張テンプレートを保存する初期の Unix システムの /etc/glob ファイルに由来しています。その後、Bash にこの機能が組み込まれましたが、名前はそのまま残りました。

パターン展開と正規表現の関係は、パターン展開が正規表現よりも早くに登場したものであり、正規表現の原型とみなすことができます。その機能は通常の機能ほど強力かつ柔軟ではありませんが、その利点はシンプルさと利便性です。

Bash を使用すると、ユーザーは拡張機能をオフにすることができます。

$ set -o noglob
# または
$ セット -f

次のコマンドで拡張機能を再度開くことができます。

$ set +o noglob
# または
$セット+f

チルダ展開

チルダ「~」は、現在のユーザーのホーム ディレクトリに自動的に展開されます。

$エコー~
/家/私

~/dir はメインディレクトリのサブディレクトリに展開することを意味し、dir はメインディレクトリ内のサブディレクトリの名前です。

# /home/me/foo ディレクトリに入ります
$ cd ~/foo

~user は、ユーザー user に展開されたホーム ディレクトリを表します。

$ echo ~foo
/ホーム/フー

$ echo ~root
/根

上記の例では、Bash はチルダの後のユーザー名に基づいてユーザーのホーム ディレクトリを返します。

~useruser が存在しないユーザー名である場合、チルダ展開は効果がありません。

$ echo ~nonExistedUser
~存在しないユーザー

~+ は現在のディレクトリに展開されます。これは pwd コマンドと同等です。

$ cd ~/foo
$ エコー ~+
/ホーム/私/フー

? 文字展開

「?」文字は、ヌル文字を除く、ファイル パス内の任意の 1 文字を表します。たとえば、「Data???」は、「Data」の後に 3 文字が続く任意のファイル名に一致します。

# ファイル a.txt と b.txt が存在します
$ls?.txt
a.txt b.txt

上記のコマンドでは、「?」は単一の文字を表すため、「a.txt」と「b.txt」の両方に一致します。

複数の文字に一致する場合は、複数の「?」を一緒に使用する必要があります。

# ファイル a.txt、b.txt、ab.txt が存在します
$ls??.txt
ab.txt

上記のコマンドでは、「??」は 2 つの文字に一致します。

「?」文字の拡張はファイル名の拡張であり、ファイルが実際に存在する場合にのみ拡張が行われます。ファイルが存在しない場合、展開は行われません。

#現在のディレクトリには.txtファイルがあります
$ エコー?.txt
a.txt

#現在のディレクトリは空のディレクトリです
$ エコー?.txt
?。TXT

上記の例では、?.txt がファイル名に展開できる場合、echo コマンドは展開結果を出力し、ファイル名に展開できない場合、echo?.txt を出力します。そのままです。

* 文字展開

「*」文字は、ファイル パス内の任意の数の文字を表します (ゼロ文字も含む)。

# ファイル a.txt、b.txt、ab.txt が存在します
$ls*.txt
a.txt b.txt ab.txt

上の例では、*.txt は接尾辞 .txt を持つすべてのファイルを表します。

現在のディレクトリにあるすべてのファイルを出力したい場合は、* を使用してください。

$ls*

* は null 文字と一致します。例を次に示します。

# ファイル a.txt、b.txt、ab.txt が存在します
$ ls a*.txt
a.txt a.txt

$ls*b*
b.txt ab.txt

* は隠しファイル (. で始まるファイル) と一致しないことに注意してください。つまり、ls * は隠しファイルを出力しません。

隠しファイルと一致させたい場合は、「.*」を記述する必要があります。

# すべての隠しファイルを表示する
$ エコー .*

隠しファイルを照合し、2 つの特別な隠しファイル ... を除外したい場合は、角括弧展開と組み合わせて使用​​し、.[!.]* と書くことができます。

$ echo .[!.]*

* 文字拡張はファイル名拡張に属し、ファイルが実際に存在する場合にのみ拡張されることに注意してください。ファイルが存在しない場合はそのまま出力されます。

# カレントディレクトリには c で始まるファイルはありません
$ echo c*.txt
c*.txt

上記の例では、カレントディレクトリに「c」で始まるファイルが存在しないため、「c*.txt」がそのまま出力されます。

* は現在のディレクトリのみに一致し、サブディレクトリには一致しません。

# サブディレクトリには.txt があります
# 不正な書き込み方法です
$ls*.txt

# 有効な書き込み方法
$ ls */*.txt

上記の例では、テキスト ファイルがサブディレクトリにある場合、*.txt は一致しないため、*/*.txt として記述する必要があります。複数のレベルのサブディレクトリがある場合は、複数のレベルのアスタリスクを記述する必要があります。

Bash 4.0 ではパラメータ globstar が導入されており、これをオンにすると、** が 0 個以上のサブディレクトリと一致できるようになります。したがって、**/*.txt は、トップレベルのテキスト ファイルと、任意の深さのサブディレクトリ内のテキスト ファイルと一致します。詳細な導入については、後述のshoptコマンドの導入を参照してください。

角括弧の展開

角括弧展開の形式は [...] で、ファイルが実際に存在する場合にのみ展開されます。ファイルが存在しない場合はそのまま出力されます。括弧内の任意の文字。たとえば、「[aeiou]」は 5 つの母音のいずれかと一致します。

# ファイル a.txt と b.txt が存在します
$ls[ab].txt
a.txt b.txt

# ファイル a.txt のみが存在します
$ls[ab].txt
a.txt

上の例では、対応するファイルが存在する場合、[ab]a または b と一致します。

角括弧の展開はファイル名一致に属します。つまり、展開された結果は既存のファイル パスと一致する必要があります。一致するものがない場合は、拡張されずにそのまま残されます。

# ファイル a.txt と b.txt が存在しません
$ls[ab].txt
ls: '[ab].txt' にアクセスできません: そのようなファイルまたはディレクトリはありません

上記例では展開されたファイルが存在しないため、[ab].txtがそのまま出力され、lsの命名時にエラーが発生します。

括弧展開には他に 2 つのバリエーション、[^...][!...] があります。これらは角括弧内にない一致する文字を表し、これら 2 つの書き方は同等です。たとえば、[^abc] または [!abc] は、abc 以外の文字と一致することを意味します。

# aaa、bbb、abaの3つのファイルがあります
$ ls ?[!a]?
アバbbb

上記のコマンドでは、[!a] は 2 文字目が a ではないファイル名を意味するため、ababbb という 2 つのファイルが返されます。

[ 文字と一致させる必要がある場合は、[[aeiou] のように角かっこで囲むことができることに注意してください。ハイフン - を一致させる必要がある場合は、[-aeiou][aeiou-] のように、角括弧内の先頭または末尾にのみ配置できます。

[開始-終了] 拡張子

角括弧の展開には短い形式の [start-end] があり、これは連続した範囲と一致することを意味します。たとえば、「[a-c]」は「[abc]」と等価で、「[0-9]」は「[0123456789]」と一致します。

# ファイル a.txt、b.txt、c.txt が存在します
$ls[a-c].txt
a.txt
b.txt
c.txt

# ファイル report1.txt、report2.txt、report3.txt が存在します
$ ls レポート[0-9].txt
レポート1.txt
レポート2.txt
レポート3.txt
...

一般的に使用される略語の例をいくつか示します。

  • [a-z]: すべて小文字。
  • [a-zA-Z]: すべての小文字と大文字。
  • [a-zA-Z0-9]: すべての小文字、大文字、数字。
  • [abc]*: abc のいずれかの文字で始まるすべてのファイル名。
  • program.[co]: ファイル program.c およびファイル program.o
  • BACKUP.[0-9][0-9][0-9]: BACKUP. で始まり、その後に 3 つの数字が続くすべてのファイル名。

この短い形式には否定形式 [!start-end] があり、この範囲に属さない文字と一致することを意味します。たとえば、「[!a-zA-Z]」は英語以外のアルファベット文字と一致することを意味します。

$ ls レポート[!1–3].txt
レポート4.txt レポート5.txt

上記のコードでは、[!1-3] は 1、2、3 を除外することを意味します。

中括弧の展開

中括弧展開 {...} は、中括弧内のすべての値に展開し、カンマを使用して各値を区切ることを意味します。たとえば、{1,2,3}1 2 3 に展開されます。

$ エコー {1,2,3}
1 2 3

$ echo d{a,e,i,u,o}g
ダグ・デグ・ディグ・ダグ・ドッグ

$ echo フロント-{A,B,C}-バック
前-A-後 前-B-後 前-C-後

中括弧の展開はファイル名展開ではないことに注意してください。対応するファイルが存在するかどうかに関係なく、指定されたすべての値に展開されます。

$ ls {a,b,c}.txt
ls: 'a.txt' にアクセスできません: そのようなファイルまたはディレクトリはありません
ls: 'b.txt' にアクセスできません: そのようなファイルまたはディレクトリはありません
ls: 'c.txt' にアクセスできません: そのようなファイルまたはディレクトリはありません

上記の例では、対応するファイルが存在しない場合でも、{a,b,c} は 3 つのファイル名に展開されるため、ls コマンドは 3 つのエラーを報告します。

もう 1 つ注意すべき点は、中括弧内のカンマの前後にスペースを入れてはいけないことです。そうしないと、中括弧の展開が失敗します。

$エコー{1, 2}
{1、2}

上記の例で、カンマの前後にスペースがある場合、Bash はこれが中括弧の展開ではなく、3 つの独立したパラメーターであると判断します。

カンマの前に値を入れることはできません。これは、展開の最初の項目が空であることを示します。

$ cp a.log{,.bak}

# に相当
# cp a.log a.log.bak

中括弧は入れ子にすることができます。

$ echo {j{p,pe}g,png}
jpg jpeg png

$ echo a{A{1,2},B{3,4}}b
aA1b aA2b aB3b aB4b

中括弧は他のパターンでも使用でき、常に他のパターンの前に展開されます。

$ echo /bin/{cat,b*}
/bin/cat /bin/b2sum /bin/base32 /bin/base64 ... ...

#基本的には以下と同等
$ echo /bin/cat;echo /bin/b*

上記の例では、中括弧の展開が最初に実行され、次に * の展開が実行されます。これは 2 つの echo コマンドを実行するのと同じです。

中括弧は複数文字のパターンに使用できますが、角括弧は使用できません (単一の文字にのみ一致します)。

$ echo {猫、犬}
猫犬

中括弧展開 {...} はファイル名展開ではないため、常に展開されます。これは、一致するファイルが存在しない場合には展開されない角括弧展開 [...] とはまったく異なります。この違いに注意してください。

# a.txtとb.txtが存在しない
$ echo[ab].txt
[ab].txt

$ echo {a,b}.txt
a.txt b.txt

上記の例では、「a.txt」と「b.txt」が存在しない場合、「[ab].txt」が通常のファイル名となり、「{a,b}.txt」は次のように展開できます。いつもの 。

{start..end} 拡張子

中括弧展開 {start..end} の短縮形があり、これは連続シーケンスへの展開を意味します。たとえば、「{a..z}」は 26 文字の小文字の英字に拡張できます。

$ echo {a..c}
a b c

$ echo d{a..d}g
dag dbg dcg ddg

$ echo {1..4}
1 2 3 4

$ エコー番号_{1..5}
数値_1 数値_2 数値_3 数値_4 数値_5

この短縮表記は逆順をサポートします。

$ echo {c..a}
c b a

$ エコー {5..1}
5 4 3 2 1

理解できない略語が見つかった場合、中括弧パターンは展開されずにそのまま出力されることに注意してください。

$ echo {a1..3c}
{a1..3c}

この短縮形式をネストして、複雑な拡張を形成できます。

$ echo .{mp{3..4},m4{a,b,p,v}}
.mp3 .mp4 .m4a .m4b .m4p .m4v

中括弧展開の一般的な使用法は、新しい一連のディレクトリを作成することです。

$ mkdir {2007..2009}-{01..12}

上記のコマンドは 36 個の新しいサブディレクトリを作成します。各サブディレクトリの名前は「年-月」です。

この記述方法のもう 1 つの一般的な使用法は、「for」ループ内で直接使用することです。

{1..4} の i について
する
  エコー $i
終わり

上の例では 4 回ループします。

整数の前に「0」が付いている場合、展開された出力の各項目の先頭には「0」が付きます。

$ エコー {01..5}
01 02 03 04 05

$ エコー {001..5}
001 002 003 004 005

この省略形では、2 番目の二重ピリオド (start..end..step) を使用して展開のステップ サイズを指定することもできます。

$ エコー {0..8..2}
0 2 4 6 8

上記のコードは '0' から '8' まで拡張し、各増分の長さは '2' であるため、合計 5 つの数値が出力されます。

複数の略語を一緒に使用すると、循環処理の効果が生じます。

$ echo {a..c}{1..3}
a1 a2 a3 b1 b2 b3 c1 c2 c3

変数の展開

Bash は、ドル記号「$」で始まるトークンを変数として扱い、変数値に展開します。詳細については、「Bash 変数」の章を参照してください。

$エコー$SHELL
/bin/bash

変数名はドル記号の後に置くだけでなく、${} の中に置くこともできます。

$ エコー ${シェル}
/bin/bash

${!string*} または ${!string@} は、指定された文字列 string に一致するすべての変数名を返します。

$ エコー ${!S*}
SECONDS SHELL SHELLOPTS SHLVL SSH_AGENT_PID SSH_AUTH_SOCK

上の例では、${!S*} は、S で始まるすべての変数名に展開されます。

サブコマンドの展開

$(...) は別のコマンドの結果に展開でき、コマンドのすべての出力が戻り値として使用されます。

$ エコー $(日付)
2020 年 1 月 28 日火曜日 00:01:13 CST

上の例では、$(date)date コマンドの結果を返します。

サブコマンドをバッククォートで配置し、コマンドの結果に展開することもできる、別の古い構文もあります。

$ echo `日付`
2020 年 1 月 28 日火曜日 00:01:13 CST

$(...) は、$(ls $(pwd)) のように入れ子にすることができます。

算術展開

$((...)) は整数演算の結果に拡張できます。詳細については、「Bash 算術演算」の章を参照してください。

$ エコー $((2 + 2))
4

文字クラス

[[:class:]] は、特定のタイプの文字の 1 つに拡張された文字クラスを表します。一般的に使用される文字クラスは次のとおりです。

  • [[:alnum:]]: 任意の英語の文字と数字に一致します
  • [[:alpha:]]: 任意の英字に一致します
  • [[:blank:]]: スペースキーとタブキー。
  • [[:cntrl:]]: ASCII コード 0 ~ 31 の印刷できない文字。
  • [[:digit:]]: 0 ~ 9 の任意の数値と一致します。
  • [[:graph:]]: A-Z、a-z、0-9、および句読点。
  • [[: lower:]]: 任意の小文字 a ~ z と一致します。
  • [[:print:]]: ASCII コード 32-127 の印刷可能な文字。
  • [[:punct:]]: 句読点 (印刷可能な文字 A ~ Z、a ~ z、0 ~ 9 を除く)。
  • [[:space:]]: スペース、タブ、LF (10)、VT (11)、FF (12)、CR (13)。
  • [[:upper:]]: 任意の大文字 A ~ Z と一致します。
  • [[:xdigit:]]: 16 進文字 (A ~ F、a ~ f、0 ~ 9)。

以下の例を参照してください。

$ echo [[:upper:]]*

上記のコマンドは、大文字で始まるすべてのファイル名を出力します。

文字クラスの最初の角括弧の後に、否定を示すために感嘆符 ! (または ^) を追加できます。たとえば、[![:digit:]] (または [^[:digit:]]) は数字以外のすべてに一致します。

$ echo [![:digit:]]*

上記のコマンドは、数字で始まらないすべてのファイル名を出力します。

文字クラスはファイル名拡張子でもあり、一致するファイル名がない場合は文字クラスが変更されずに出力されます。

# 大文字で始まるファイルはありません
$ echo [[:upper:]]*
[[:アッパー:]]*

上記例では、一致するファイルが存在しないため、文字クラスをそのまま出力します。

使用上の注意

ワイルドカードを使用する場合は、注意すべき点がいくつかあります。

**(1) ワイルドカードは最初に解釈されてから実行されます。 **

Bash がコマンドを受信し、その中にワイルドカードが含まれていることを検出すると、ワイルドカードを展開してコマンドを実行します。

$ ls a*.txt
ab.txt

上記コマンドの実行プロセスは、Bash がまず「a*.txt」を「ab.txt」に展開し、次に「ls ab.txt」を実行します。

**(2) ファイル名の拡張子が一致しない場合は、そのまま出力されます。 **

一致するファイルがない場合は、拡張子はそのまま出力されます。

# r で始まるファイル名はありません
$エコーr*
r*

上記コードでは「r」で始まるファイル名がないため、「r*」がそのまま出力されます。

別の例を示します。

$ls*.csv
ls: *.csv: そのようなファイルまたはディレクトリはありません

また、前述したように、中括弧展開 {...} はファイル名展開ではありません。

**(3) は単層パスにのみ適用されます。 **

すべてのファイル名拡張子は、単一レベルのパスとのみ一致し、ディレクトリ全体で一致することはできません。つまり、サブディレクトリ内のファイルと一致することはできません。つまり、「?」や「*」などのワイルドカード文字は、パス区切り文字 (「/」) と一致することはできません。

サブディレクトリ内のファイルを照合したい場合は、次のように記述できます。

$ ls */*.txt

Bash 4.0 では、新しい globstar パラメータが追加されており、これにより ** が 0 個以上のサブディレクトリに一致することが可能になります。詳細については、後述の shopt コマンドの紹介を参照してください。

**(4) ファイル名にはワイルドカード文字を使用できます。 **

Bash では、ファイル名にワイルドカード文字を使用できます。これは、ファイル名に特殊文字が含まれることを意味します。ファイル名を引用する場合は、ファイル名を一重引用符または二重引用符で囲む必要があります。

$ 'fo*' をタッチ
$ls
フォ*

上記のコードは fo* ファイルを作成します。ここで、* はファイル名の一部です。

量指定子の構文

量子構文は、パターン一致の数を制御するために使用されます。これは、Bash の「extglob」パラメータがオンになっている場合にのみ使用できますが、通常はデフォルトでオンになっています。次のコマンドを使用してクエリを実行できます。

$ ショップ外部グロブ
外部グロブオン

「extglob」パラメータがオフになっている場合は、次のコマンドを使用してオンにできます。

$ shopt -s extglob

以下に示すように、量指定子の構文がいくつかあります。

  • ?(pattern-list): パターンは 0 回または 1 回一致します。
  • *(pattern-list): パターンは 0 回以上一致します。
  • +(pattern-list): パターンは 1 回以上一致します。
  • @(pattern-list): パターンに 1 回だけ一致します。
  • !(pattern-list): 指定されたパターン以外のすべてに一致します。
$ ls abc?(.)txt
abctxt abc.txt

上の例では、?(.) は 0 点または 1 つの点に一致します。

$ ls abc?(def)
abc abcdef

上の例では、?(def) は 0 または 1 つの def と一致します。

$ ls abc@(.txt|.php)
abc.php abc.txt

上の例では、@(.txt|.php) は 1 つだけの .txt または .php 接尾辞を持つファイルと一致します。

$ ls abc+(.txt)
abc.txt abc.txt.txt

上の例では、+(.txt) は 1 つ以上の .txt サフィックスを持つファイルと一致します。

$ ls a!(b).txt
a.txt abb.txt ac.txt

上記の例では、「!(b)」は 1 文字の「b」以外のすべてに一致することを意味するため、「ab.txt」を除く他のファイル名も一致できます。

量指定子の構文はファイル名拡張子でもあり、一致するファイルがない場合はそのまま出力されます。

# abcで始まるファイル名はありません
$ ls abc?(def)
ls: 'abc?(def)' にアクセスできません: そのようなファイルまたはディレクトリはありません

上記の例では、該当するファイルが存在しないため、「abc?(def)」がそのまま出力され、「ls」コマンドがエラーとなります。

ショップコマンド

shopt コマンドは Bash の動作を調整できます。ワイルドカード展開に関連するパラメータがいくつかあります。

shoptコマンドの使い方は以下の通りです。

#パラメータを開く
$ shopt -s [オプション名]

# パラメータをオフにする
$ shopt -u [オプション名]

# パラメータがオフかオンかを問い合わせる
$shopt[オプション名]

(1)ドットグロブパラメータ

dotglob パラメータを使用すると、展開結果に隠しファイル (ドットで始まるファイル) を含めることができます。

通常、展開された結果には隠しファイルは含まれません。

$ls*
abc.txt

「dotglob」をオンにすると、隠しファイルが含まれます。

$ shopt -s ドットグロブ
$ls*
abc.txt.config

(2)nullglob パラメータ

「nullglob」パラメータを使用すると、ワイルドカードがどのファイル名にも一致しない場合に null 文字を返すことができます。

デフォルトでは、ファイル名に一致しないワイルドカードは変更されません。

$rm b*
rm: 'b*' を削除できません: そのようなファイルまたはディレクトリはありません

上記の例では、カレントディレクトリに「b」で始まるファイル名が含まれていないため、「b*」のファイル名展開は行われず、変更されないままとなるため、「rm」コマンドはファイルが存在しないというエラーを報告します。 b*

「nullglob」パラメータをオンにすると、一致しないワイルドカードが空の文字列を返すことができます。

$ shopt -s nullglob
$rm b*
rm: オペランドがありません

上記の例では、「b*」に一致するファイル名が存在しないため、「rm b*」が「rm」に展開され、「オペランドがありません」というエラーが発生します。

(3)failglob パラメータ

「failglob」パラメータによってワイルドカードがどのファイル名とも一致しない場合、Bash は各コマンドにエラーを処理させるのではなく、エラーを直接報告します。

$ shopt -s フェイルグロブ
$rm b*
bash: 一致しません: b*

上の例では、failglob を開いた後、b* がどのファイル名にも一致しないため、Bash は直接エラーを報告し、rm コマンドによる処理を許可しなくなります。

(4) extglob パラメータ

extglob パラメータを使用すると、Bash が ksh の拡張構文をサポートできるようになります。デフォルトでオンになっているはずです。

$ ショップ外部グロブ
外部グロブオン

その主な用途は、量指定子の構文をサポートすることです。量指定子構文をサポートしたくない場合は、次のコマンドを使用してそれをオフにできます。

$ shopt -u extglob

(5)nocaseglob パラメータ

nocaseglob パラメータは、ワイルドカード拡張で大文字と小文字を区別しないようにします。

$ shopt -s nocaseglob
$ ls /windows/プログラム*
/windows/プログラムデータ
/windows/プログラム ファイル
/windows/プログラム ファイル (x86)

上の例では、nocaseglob をオンにすると、p​​rogram* は大文字と小文字が区別されなくなり、ProgramData などと一致します。

(6)グロブスターパラメータ

globstar パラメータを使用すると、** を 0 個以上のサブディレクトリに一致させることができます。このパラメータはデフォルトではオフになっています。

次のファイル構造を想定します。

a.txt
sub1/b.txt
サブ1/サブ2/c.txt

上記のファイル構造では、最上位ディレクトリ、第 1 レベルのサブディレクトリ sub1、および第 2 レベルのサブディレクトリ sub1\sub2 にテキスト ファイルがあります。ワイルドカードを使用して表示するにはどうすればよいですか?

デフォルトでは以下のようにしか書けません。

$ ls *.txt */*.txt */*/*.txt
a.txt サブ1/b.txt サブ1/サブ2/c.txt

これは、「*」が現在のディレクトリにのみ一致するため、サブディレクトリを一致させたい場合は、レイヤーごとにのみ書き込むことができるためです。

globstar パラメータをオンにすると、** は 0 個以上のサブディレクトリと一致します。したがって、**/*.txt は望ましい結果を得ることができます。

$ shopt -s グロブスター
$ ls **/*.txt
a.txt サブ1/b.txt サブ1/サブ2/c.txt

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0