概要: この記事では、 Claude Codeなど、CLI (コマンドライン)ベースのAIエージェントを使う時に 知っていると便利な UNIX/Linux に関する基礎知識を紹介する。 最終的な目標は各コマンドが何をやっているかおおまかに把握できることである。 記事は3部に分かれており、 1. UNIXの基本、 2. シェルの使い方、 および 3. よく使うコマンド一覧 となっている。
なお、ここでは macOS ターミナルを使うことを想定している。
ファイルの数が多くなると管理が大変になるので、 フォルダ (あるいはディレクトリとも呼ばれる) を使って整理する。
UNIXでは、すべてのファイル・フォルダはひとつの巨大な木構造 (tree) をなしている。
ファイルシステム中に同じ名前のファイル・フォルダが複数存在している場合、 ファイルの正確な位置は、パス名 (path) で表す。 パス名は、ファイルシステム上におけるファイルの「住所」である。
X のパス名を書け。
H のパス名を書け。
C のパス名を 2つ書け。
UNIX では、いくつかのパス名は 「お約束」として決められている。
/bin: 実行可能ファイル (プログラム)/Users/ユーザ名: 各ユーザのホームディレクトリ (macOS以外では /home/ユーザ名)/etc: 設定ファイル/dev: デバイスファイル% で表している。
Linuxの場合はプロンプトは通常 $ なので、適宜読みかえてほしい。
ls パス名 … パス名に含まれているファイル一覧を表示する。
% ls /bin [ dd launchctl pwd tcsh bash df link realpath test cat echo ln rm unlink ...
ls -l パス名 … より詳細なメタデータとともに表示する。
% ls -l /bin total 9536 -rwxr-xr-x 2 root wheel 101472 Mar 21 15:13 [ -r-xr-xr-x 1 root wheel 1310352 Mar 21 15:13 bash -rwxr-xr-x 1 root wheel 119008 Mar 21 15:13 cat -rwxr-xr-x 1 root wheel 120576 Mar 21 15:13 chmod -rwxr-xr-x 1 root wheel 136704 Mar 21 15:13 cp ...
UNIX のコマンドは "-" や "+" で始まる
文字列を与えることにより異なった動きをするものが多い。
(これは何か規則があるわけではなく、ただの慣例である。)
このような文字列を一般にコマンドの「オプション (option)」と呼ぶ。
cat パス名 … ファイルの内容を表示する。
UNIX ではどんなファイルも同様に扱われるので、 実は% cat /etc/shells # List of acceptable shells for chpass(1). # Ftpd will not allow users to connect who are not using # one of these shells. /bin/bash /bin/csh /bin/dash ...
catコマンドを使えば
あらゆるファイルの内容を (バイト列として) 表示できる。
しかし実際には JPEG などの画像ファイルをバイト列として表示しても
意味不明な文字列が表れるだけである。
/Applications/Safari.app/Contents/MacOS/…
実は「パス名」と呼ばれているものには 2つの種類がある。 上で説明した「パス名」は「絶対パス名」のことであった。
つまり、上のディレクトリ X の位置は、次の2通りで表せる:
/A/E/X
./X
. (ドット)」で表す:
./X, ./K など
..」で表せる。
../../../../../../../Application =
/Application
カレント・ディレクトリが E のとき…
A への相対パス名は?
H への相対パス名は?
K への相対パス名は? (2つある)
cd パス名 … カレント・ディレクトリを指定されたパス名に変更する。
pwd … カレント・ディレクトリを表示する。
% pwd /Users/euske % cd /etc % pwd /etc
ls (パス名を省略) … カレント・ディレクトリに含まれているファイル一覧を表示する。
ls -l (パス名を省略) … より詳細なメタデータとともに表示する。
ファイルシステム上で「実行可能 (executable)」フラグ (x) がついているファイルは、
プログラムとして実行可能である。
通常、ここには機械語で書かれた命令列が記録されている:
-rwxr-xr-x 1 root wheel 154352 Mar 21 15:13 /bin/ls
実行されたプログラムは、OS上で プロセス (process) として走り続ける。 UNIXはマルチタスクOSなので、通常は複数のプロセスが並列に実行される。
(実際の CPU は一度にひとつの プログラムしか実行できないため、各プロセスは高速に切り替えられ 少しずつ (10ms程度) 実行される (時分割処理)。 これらプロセス切り替え処理は、OS の カーネル (kernel) という部分がおこなう。)
ただしPC上のプロセス(アプリ)が入出力装置としてもっぱらGUIを使うのに対し、 サーバ上のプロセスは入出力装置としてネットワークを使う。 この違いを除けば、PCでもサーバでも UNIXプロセスが動いているという点は同じである。
プロセスが入出力装置にアクセスするときは、ほとんどの場合 OS (カーネル) を介している。
(たいていのLinuxでは init のかわりに systemd、
macOSでは launchd が使われている。)
ps -x … 自分が所有するプロセス一覧を表示する。
% ps -x PID TT STAT TIME COMMAND 1021 ?? Ss 0:14.87 /System/Library/ExtensionKit/…/Contents/MacOS/WallpaperVideoExtension 1110 ?? S 8:48.23 /usr/sbin/distnoted agent 1164 ?? S 23:39.51 /usr/sbin/cfprefsd agent … 78971 s002 R+ 0:00.00 ps -x
ps -ax … すべてのプロセス一覧を表示する。
% ps -ax
PID TT STAT TIME COMMAND
1 ?? Ss 50:47.04 /sbin/launchd
661 ?? Ss 25:50.01 /usr/libexec/logd
673 ?? Ss 16:53.34 /usr/libexec/configd
698 ?? Ss 0:58.62 /usr/sbin/syslogd
…
79138 s002 R+ 0:00.00 ps -ax
最近では、サーバ上では生身のプロセスを直接動かすのではなく、 Dockerなどの「コンテナ」を実行することが多い。
現代のUNIXはさまざまな入出力装置をサポートしているが、 なかでもテキストの入出力をおこなう端末 (ターミナル, TTY) は もっとも基本的な装置であり、昔から多くの場面で利用されてきた。
端末は、コンピュータと接続して文字情報をやりとりする機器のことである。 かつてはタイプライタのような物理的な機械だったが、現在ではGUIにより 仮想的にエミュレートされるアプリになっている。
UNIX の各プロセスは、つねに 「標準入力 (stdin)」 「標準出力 (stdout)」 という 2つの架空の入出力装置が使用可能である。 「架空」というのは、これらが単一の装置ではなく、 さまざまな装置に切り替え可能だからである。 これらをまとめて標準入出力 (Standard I/O)と呼ぶ。
通常のPCでは、プロセスの標準入力・標準出力は、 どれも端末エミュレータアプリに接続されている。 これらはプロセスが画面に文字を表示したり、 キーボードから文字を入力するために使用する。
標準出力は、プロセスからの出力を表示するのに使われる:
% date Wed Aug 12 14:02:25 JST 2026
標準入力は、プロセスがユーザから入力するのに使われる:
% bc >>> 2+3 (注意: Linuxの場合、>>> は表示されない) 5
いっぽうサーバでは、普通は画面が接続されていないので、プロセスの標準出力は、 ログファイルか、ネットワークを介した AWS CloudWatch などのログ収集サービスに 接続されている。
シェルの機能 (後述) を使うと、 標準出力を端末ではなくファイルに切り替える (リダイレクトする) ことができる:
% date > output.txt (テキストファイル output.txt が生成される)
> の出力先として
うっかり存在するファイル名を指定してしまうと、
そのファイルは何の警告もなく上書きされ、
空のファイルにされる。
また、標準入力を端末ではなくファイルにリダイレクトすることも 可能である:
(テキストファイル input.txt を作成する) % cat input.txt 2+3 % bc < input.txt 5
さらに、UNIX には「パイプ」という機能がある。 これを使うと 「あるプロセスの標準出力を、別のプロセスの標準入力に」 リダイレクトすることができる:
% date | bc
Parse error: bad expression
<stdin>:1
パイプによる複数プロセスの接続は UNIX (シェル) の特徴的な機能のひとつである。 これをうまく使うと、複雑な処理をいくつかのコマンドの組み合わせによって 実現することができる。
(ls コマンドの出力を検索し、さらにそれをソートして最初の10行を表示する) % ls -l | grep euske | sort | head -n10
<, >, | による
標準入力・標準出力の切り替えは、
プロセスを起動する瞬間にしか指定できない。
いちどプロセスが起動してしまうと、あとから
切り替えることはできないので注意。
% ls -l > output.txt
grepコマンドを使うと、
テキストファイル中で特定のパターンが含まれる行だけを表示できる:
% grep .conf output.txt -rw-r--r-- 1 root wheel 1051 Mar 21 15:13 asl.conf -rw-r--r-- 1 root wheel 1935 Mar 21 15:13 autofs.conf -rw-r--r-- 1 root wheel 0 Mar 21 15:13 kern_loader.conf -r--r--r-- 1 root wheel 2451 Mar 21 15:13 man.conf …
% ls -l | grep .conf -rw-r--r-- 1 root wheel 1051 Mar 21 15:13 asl.conf -rw-r--r-- 1 root wheel 1935 Mar 21 15:13 autofs.conf -rw-r--r-- 1 root wheel 0 Mar 21 15:13 kern_loader.conf -r--r--r-- 1 root wheel 2451 Mar 21 15:13 man.conf …
ls | sort を実行したとき...
ls の標準入力および標準出力はそれぞれ何か?
sort の標準入力および標準出力はそれぞれ何か?
UNIX を使ううえで中心的な役割を果たしているのが シェル (shell) と呼ばれるプログラム (アプリ) である。
シェルの基本動作:
ls -l /usr/bin
(ls, -l, /usr/bin)
ls
重要なのは 3. のステップである。UNIXでは複数のプロセスが並列動作するので、
子プロセス (ls) が動いている間もシェルは動いている。
ただし、lsとシェルは同じ端末を共有している。
そのため、シェルはlsプロセスが終了するまで、端末の使用を邪魔しないように
意図的に「待っている」のである。
ユーザからみると、これはコマンドが一時的に端末を「乗っ取った」ように見える。
% ls -l /usr/bin (子プロセスが起動する)(lsが端末を乗っ取る) total 172688 -rwxr-xr-x 1 root wheel 171888 Jun 25 11:29 AssetCacheLocatorUtil -rwxr-xr-x 1 root wheel 227664 Jun 25 11:29 AssetCacheManagerUtil ... -rwxr-xr-x 1 root wheel 3314 Jun 25 11:29 znew -rwxr-xr-x 1 root wheel 170320 Jun 25 11:29 zprint (lsが終了する)(シェルが再開する) %
これは、画面全体を占拠するようなコマンド (たとえば、claudeやvim) のようなものでも
同じである:
% claude (子プロセスが起動する)(claudeが端末を乗っ取る) ... (claudeが終了する)(シェルが再開する) %
UNIX 上で「コマンド (command)」と呼ばれるものは、 実はほとんどシェルによって起動される子プロセスである。 たいていのコマンドは数ミリ秒〜数秒しか生存しないため、 UNIX は「湯水のようにプロセスを消費する」OSといえる。
lsなどのコマンド: 数ミリ秒〜数秒
そして UNIX的にみれば、シェルもまたひとつのプロセスにすぎず、特別な存在ではない。 その証拠に、シェルには複数の種類が存在する。
(ターミナルを開く) % bash (bashを起動する) $ tcsh (tcshを起動する) % ksh (kshを起動する) $ ls (lsを実行する) ... $ exit (kshを終了する) % exit (tcshを終了する) $ exit (bashを終了する) % exit (ターミナルを閉じる)
先に述べたように、シェルの主な機能はコマンド文字列を解析し、プロセスを起動することである。 たとえば:
という行は、以下の行と同じである:% ls -a /etc
さらにこれは、プログラミング的には、関数呼び出しの一種と考えることができる:% /bin/ls -a /etc
ls("-a", "/etc")
この行が入力されたとき、シェルは以下のことをおこなう:
/bin/ls というプログラムを子プロセスとして起動する。
ls … 0番目の引数 (コマンド名あるいはプログラムのパス名)
-a … 1番目の引数
/etc … 2番目の引数
ここで注意したいのは、引数である
「-a」や「/etc」をどのように利用するかは
各コマンド次第ということである。
すべてのコマンドにおいて
「-a」がオプションであると決まっているわけではないし
「/etc」がパス名として解釈されるとも限らない。
この意味で、UNIX の使い方を学ぶことは
API の使い方を学ぶのに似ている。
個々の関数・メソッドの引数がどのような意味をもつのか学習し、
それらを組み合わせて必要な処理を実現する。
-a」という名前のディレクトリがあったとすると、
そのままでは ls のパス名として指定できない。
だが ls ./-a というトリックを使えば指定できる。)
たとえば echo というコマンドは、シェルから与えらえた引数をただそのまま
表示するだけのコマンドである:
% echo abc 1234 abc 1234 % echo -a /etc -a /etc
一般にシェルの引数は「 」(スペース) で
区切られるが、"〜" で囲むことにより
スペースが入っている文字列を「ひとつの引数」として認識させることができる:
% ls -a /etc … % ls "-a /etc" ls: invalid option -- ' ' % "ls -a" /etc ls -a: command not found
以下のコマンドの引数を 0番目からすべて挙げてみよう:
ls /etc /bin
ls "/etc /bin"
ls "/etc" "/bin"
ls "" "/bin"
"ls /etc /bin"
ls /etc /bin > output.txt
実は、
> output.txt のような部分は
コマンドの引数ではない。
リダイレクト指定はプロセスが起動する前にシェルによって処理され、
コマンドには渡らない。
(つまり、シェルから起動されたプロセスは自分の標準入出力が実際はどこに接続されているのか知らない。)
シェルでは、ディレクトリ上に複数のファイルがあるとき、 それらのファイル名を複数の引数として展開する機能がある (ファイル名展開, pathname expansion)。
たとえば、カレント・ディレクトリ内に
a, bb, ccc
というファイルがあるとき…
以下の(ファイルを準備) % echo > a % echo > bb % echo > ccc % ls a bb ccc
* (ワイルドカード) を指定すると
以下の引数を与えたのと同じである:% cat *
% cat a bb ccc
ワイルドカードには、パターンを指定することもできる。
たとえば /etc 内に多くの「.conf」で終わるファイルがあるとき…
以下のパターンは% cd /etc % ls -l | grep .conf -rw-r--r-- 1 root wheel 1051 Mar 21 15:13 asl.conf -rw-r--r-- 1 root wheel 1935 Mar 21 15:13 autofs.conf …
以下のような引数に展開される:% cat *.conf
% cat asl.conf autofs.conf …
繰り返すが、ここで展開されたパス名をどのように扱うかは 各コマンド次第である。 シェルがファイル名展開したからといって、 各コマンドがこれらをファイル名として扱う保証はない。
ここで、echo コマンドは
シェルが各引数をどのように展開したかを表示するのに使える:
% echo /etc/*.conf /etc/asl.conf /etc/autofs.conf …
/etc 以下に次のようなファイルがあるとする:
a.conf
b.conf
issue
/etc であるとして、
以下のコマンドラインの出力を予想してみよう:
echo *
echo *.conf
echo *.conf /etc/*
echo /etc/issue*
echo "*"
とある空のディレクトリ内で、 以下のコマンド列を実行すると何が表示されるか予想してみよう:
% echo > hello % echo > world % echo *
UNIX では、各プロセスに環境変数 (environment variable) というものが付属している。 これは文字列型の変数で、プログラムの変数と同様に、好きな数だけ作成することができ、 いろいろな設定を保存しておくのに使われる。 慣例により、環境変数の名前には大文字が使われることが多い (実際には、英数字であればなんでもよい)。
現在のシェルに付属している環境変数一覧を見るためには envコマンドを使う:
% env SHLVL=3 HOME=/root OLDPWD=/root PAGER=less …
順序がばらばらで見にくいので sortで並べ変えてみる:
% env | sort HOME=/root OLDPWD=/root PAGER=less …
各プロセスに付属している環境変数の値を見るには、以下のコマンドを実行する:
% ps -axeww
シェルでは、コマンドライン引数に含まれる環境変数の値が展開される。
たとえば環境変数 USER に euske という値が入っている場合、
% ls /Users/${USER}/foo
は、以下のように展開される:
% ls /Users/euske/foo
echoコマンドは、環境変数の内容を確認するためによく使われる:
% echo ${USER}
euske
未定義の環境変数を展開してもエラーにはならず、空文字列として扱われる:
% echo /Users/${USSR}/foo
/Users//foo
なお、${〜}の中カッコは省略可能である:
% echo $USER euske
以下の環境変数に:
USER=jon
FILE=athan
echo /home/${USER}/${FILE}
echo $USER$FILE
echo "$USER"ny
echo $USERny
各プロセスの環境変数は、一般にそのプロセスの起動時にだけ設定できる。
プロセスの起動時に環境変数を設定するには、実行したいコマンド引数の前に
「変数名=値」のような形式を追加する。
たとえば date コマンドは、
環境変数 TZ の値によって異なる時間帯の時刻を返す:
% date Wed Aug 12 14:03:35 JST 2026 % TZ=UTC date Wed Aug 12 05:03:42 UTC 2026 % TZ=America/New_York date Wed Aug 12 01:03:57 EDT 2026
また、親プロセスが持っている環境変数はすべて子プロセスに継承される。 そのため親プロセスに環境変数を設定しておけば、すべての子プロセスで共通の値が使える:
% echo $XYZ (環境変数XYZは未定義) % XYZ=abc123 zsh (環境変数XYZにabc123を設定して zshを起動) % echo $XYZ (環境変数XYZに値が入っている) abc123
例外はシェルで、シェルの中ではいつでも環境変数を作成・変更できる。
実際には、環境変数はより一般的なシェル変数 (shell variable) の一部として扱われる。
シェル変数の一覧 (環境変数も含む) を表示するには
setコマンドを使う:
% set BASH=/bin/bash COLUMNS=100 DISPLAY=:1 EDITOR=vi …
シェル変数は、ただ
「変数名=値」のように書けば
値を設定できる。
% XYZ=abc123 (シェル変数XYZにabc123を設定) % echo $XYZ (シェル変数XYZの値を表示する)
ただし、すべてのシェル変数が環境変数として扱われるわけではないことに注意。 環境変数として扱われるのは、シェル変数のうち 「export属性」をもつものだけである。 Export属性をもったシェル変数は、何も指定しなくても 以後シェルから起動したすべてのプロセスに環境変数として渡される:
% XYZ=abc123 (シェル変数XYZにabc123を設定) % echo $XYZ (シェル変数XYZの値を表示する) abc123 % env (XYZは環境変数ではない) … % export XYZ (XYZにexport属性をつける) % env (XYZが環境変数として渡されている) … XYZ=abc123 …
UNIX の環境変数のなかでも PATH と HOME は
とくに重要である。
通常、UNIX シェルでは第0引数としてコマンド (プログラム) のパス名を指定する:
しかし実際には以下のように書いても動く:% /bin/ls -l /etc
これを可能にしているのは環境変数% ls -l /etc
PATH のためである。
環境変数 PATH の中身を見てみると、
コロン (:) で区切られたパス名 (ディレクトリ名) の一覧が含まれているのがわかる:
% echo $PATH /usr/local/bin:/usr/bin:/bin
これは「あるコマンドが実行されたとき、
/usr/local/bin/コマンド名、
/usr/bin/コマンド名、
/bin/コマンド名
のいずれかのプログラムを実行せよ」ということを表している。
PATH にはこのようなコマンドの「検索順序」が記録されている。
その証拠に、PATH をおかしな値にすると動かない:
% PATH=xxx % ls ls: command not found % /bin/ls …
なお、PATH はよくシステムのデフォルト値をカスタマイズして
使うことが多い。PATH に独自のディレクトリを追加するには、以下のようにする:
PATH=$PATH:/Users/euske/bin (/Users/euske/bin もコマンド検索順序に追加する)
UNIX では、各ユーザのホームディレクトリは
/Users/ユーザ名
(Linuxの場合は /home/ユーザ名) とされているが、
これはただの慣例であり、実際にはどこであってもかまわない。
環境変数 HOME は、そのユーザの所有する
ホームディレクトリのパス名を指定する。
これは起動時には通常そのユーザのデフォルト値が設定されているが、
この値を変更することで、どんなディレクトリでもホームディレクトリとして
使用することができる:
% echo $HOME (環境変数HOMEの内容を表示する) /Users/euske % cd (ホームディレクトリに移動) % pwd (カレント・ディレクトリを表示) /Users/euske % HOME=/etc (環境変数HOMEを変更する - ホームディレクトリが変更される) % cd (ホームディレクトリに移動) % pwd (カレント・ディレクトリを表示) /etc
また、シェルのコマンドライン上では ~ が
自分のホームディレクトリに展開されるが、
この値も実際には環境変数 HOME を展開しているだけである:
% echo $HOME/work /Users/euske/work % echo ~/work /Users/euske/work
UNIX では、各ユーザのデフォルトのホームディレクトリ (やシェル) は
/etc/passwd というテキストファイル内に記載されている。
UNIX におけるホームディレクトリとは、つまるところ:
HOME が、そのパス名に設定されている
ls (LiSt)
% ls … (カレント・ディレクトリのファイル一覧を表示) % ls /etc … (ディレクトリ /etc のファイル一覧を表示)
cd (Change Directory)
% cd /etc (カレント・ディレクトリを /etc に変更) % cd (カレント・ディレクトリを自分のホームディレクトリに変更)
pwd (Print Working Directory)
% pwd (現在のパス名を表示)
cat (conCATenate) ・ less
cat は
複数のファイルを連結 (conCATenate) するためのコマンドだった。
% less /etc/services (/etc/services の内容を表示) % cat /etc/hosts /etc/services (/etc/hosts の内容と /etc/services の内容を連結して表示)
なお、lessコマンド内では以下のキー操作が使える:
mkdir (MaKe DIRectory)
% mkdir foo (カレント・ディレクト下に foo を作成)
cp (CoPy)
% cp a.txt b.txt (a.txt を新しい b.txt という名前で複製する) % cp a.txt dest/ (a.txt を dest/ ディレクトリ内に同じ名前で複製する) % cp a.txt b.txt c.txt dest/ (a.txt, b.txt, c.txt の各ファイルを dest/ 内に同じ名前で複製する) % cp *.txt dest/ (〜.txt で終わるすべてのファイルを dest/ 内に同じ名前で複製する)
cp で複製できるのは、通常はファイルのみである。
あるディレクトリ内のファイルを (ディレクトリごと) 複製したい場合には
-r オプションを使う:
% cp -r foo dest/ (foo ディレクトリ内とその中のファイルをすべて dest/ 内に複製する)
-i オプションを使う:
% cp -i a.txt b.txt (a.txt を新しい b.txt という名前で複製するが、 すでに同名のファイルがある場合は確認する)
mv (MoVe)
% mv a.txt b.txt (a.txt を新しい b.txt という名前に変更する) % mv a.txt dest/ (a.txt を dest/ ディレクトリ内に移動する) % mv a.txt b.txt c.txt dest/ (a.txt, b.txt, c.txt の各ファイルを dest/ 内に移動する) % mv *.txt dest/ (〜.txt で終わるすべてのファイルを dest/ 内に移動する)
-i オプションを使う:
% mv -i a.txt b.txt (a.txt を新しい b.txt という名前に変更するが、 すでに同名のファイルがある場合は確認する)
rm (ReMove)
% rm a.txt (ファイル a.txt を削除する) % rm *.txt (〜.txt で終わるすべてのファイルを削除する)
rm で削除できるのは、通常はファイルのみである。
あるディレクトリ内のファイルを (ディレクトリごと) 削除したい場合には
-r オプションを使う:
% rm -r dest/ (dest/ ディレクトリ内とその中のファイルをすべて削除する)
rm コマンドは削除に対して何も確認しない
(ディレクトリまるごとの削除でも同じ)。
これは非常に危険なので、削除の際には、必ずファイルをひとつずつ確認する
-i オプションを使うのがおすすめである:
% rm -i *.txt (〜.txt で終わる各ファイルを yes/no で確認しながら削除する)
du (DiskUse)
% du ~ (ホームディレクトリの使用量を各ディレクトリごとに表示する) % du -sh . (カレント・ディレクトリの総使用量のみを表示する)
cp, mv, rm などのコマンドでは
「-」で始まる引数はオプションとみなされる。
もし実際に "-i" というファイルを複製したい場合、
cp -i a.txt などとやってもうまくいかない:
% cp -i a.txt cp: missing destination file operand after 'a' (エラーが出て実行できない)
このような場合には、2つの方法がある:
-」で始まらなけれさえすればよいので、
-i がカレント・ディレクトリにあることを利用して、
以下のようにする:
% cp ./-i a.txt (カレント・ディレクトリの -i というファイルがコピーされる)
--" が表れると、
それ以後の引数をオプションとして解釈することをやめ、
ファイル名として解釈するように実装されている。
このことを利用して:
% cp -- -i a.txt (-i はオプションでなく実際のファイル名として解釈される)