マカーのみなさん、ターミナルからコマンドを打とうとした時、オプションがわからなくなったことありますよね?そんなとき、あなたならどうしますか?
- ググる
-
-h
や--help
オプションをつけてみる -
man
コマンドを使う
色々方法はありますが、今回は man
コマンドを推す記事になっております。
エンジニアたるや、一度CLIを開いたんだからキーボードからはなるべく手を離さない方がイケてますよね(w3m
があるやろやい、という変態な方、どうぞご自愛くださいませ)。シェル芸人への第一歩と言っても過言ではないかもしれません。
manコマンドとは?
$ man ls
このように、ターミナルの全画面でコマンドの説明が表示されます。
コマンドによって構成はまちまちですが、基本的にはコマンドとオプションのフォーマット、説明、各オプションの詳細説明という流れになっています。
それから、 man
コマンドはローカルにインストールされていますので ネットがなくても見られます 。これは「ググる」に対する大きなアドバンテージですね!電車や飛行機の移動でオフラインになったときのMacbookをただの文鎮にしたくなければ、是非manで各コマンドのマニュアルを読んでみましょう。
LS(1) General Commands Manual LS(1)
NAME
ls – list directory contents
SYNOPSIS
ls [-@ABCFGHILOPRSTUWabcdefghiklmnopqrstuvwxy1%,] [--color=when] [-D format] [file ...]
DESCRIPTION
For each operand that names a file of a type other than directory, ls displays its name as well as any requested, associated information. For each operand that names a file of type
directory, ls displays the names of files contained within that directory, as well as any requested, associated information.
If no operands are given, the contents of the current directory are displayed. If more than one operand is given, non-directory operands are displayed first; directory and non-directory
operands are sorted separately and in lexicographical order.
The following options are available:
-@ Display extended attribute keys and sizes in long (-l) output.
-A Include directory entries whose names begin with a dot (‘.’) except for . and ... Automatically set for the super-user unless -I is specified.
...
英語につらみ?(日本語コマンド manj)
man
コマンドは詳細が載っていていいんだけど、全部英語で辛いやん?という方に朗報です。
Macの方はこちら
$ brew install sh0nk/tap/man-japanese
これで、
$ manj ls
このように manj
コマンドが使えるようになり、日本語マニュアルが表示できるようになります。(対応していないコマンドもあります。)
LS(1) ユーザーコマンド LS(1)
名前
ls - ディレクトリの内容をリスト表示する
書式
ls [オプション]... [ファイル]...
説明
FILE (デフォルトは現在のディレクトリ) に関する情報を一覧表示します。 -cftuvSUX のいずれも指定されず、 --sort も指定されていない場合、 要素はアルファベット順でソートされます。
長いオプションで必須となっている引数は短いオプションでも必須です。
-a, --all
. で始まる要素を無視しない
...
ちなみにこちらの manj
コマンドは私がHomebrewで用意したもので、こちらの記事で作った過程も紹介していますのでよろしければどうぞ。
ざっくりとした見方
ここで、書式(コマンドのフォーマット)について軽く触れておきます。lsのケースはシンプル過ぎるので、manコマンド自体の例を持って説明してみます。
書式
man [-acdfFhkKtwW] [--path] [-m system] [-p string] [-C config_file] [-M pathlist] [-P pager] [-B browser] [-H htmlpager] [-S section_list] [section] name ...
最初に登場する man
がコマンド自身、その後は引数とオプションです。このとき []
で囲まれている部分が 任意オプション ですので、指定しても、しなくてもよいものです。その後の、 []
がない部分が 必須 のパラメータです。
そのため、 man
コマンドの場合は、 name
として何のコマンドのマニュアルが読みたいかを渡してあげる必要があります。また、 ...
があるので、 name
は複数指定することが可能です。
これらの文法は、POSIXとして規定されています。
lsとcdの違い
lsとcdの違いは何ですか?と聞かれた時すぐにわかるでしょうか?lsはファイルリストを表示して、cdはカレントディレクトリを・・
そうなんですが、そもそもコマンドのつくりが違います。
lsは通常のコマンドなのですが、cdはシェルに付属した、シェルビルトインコマンドです。そのことはmanコマンドの結果からもわかります。言われてみれば、カレントディレクトリを変更するということ自体がシェルの中での話なので、当たり前といえば当たり前のことですね。
NAME
builtin, !, %, ., :, @, {, }, alias, alloc, bg, bind, bindkey, break, breaksw, builtins, case, cd, chdir, command, complete, continue, default, dirs, do, done, echo, echotc, elif, else,
end, endif, endsw, esac, eval, exec, exit, export, false, fc, fg, filetest, fi, for, foreach, getopts, glob, goto, hash, hashstat, history, hup, if, jobid, jobs, kill, limit, local, log,
login, logout, ls-F, nice, nohup, notify, onintr, popd, printenv, pushd, pwd, read, readonly, rehash, repeat, return, sched, set, setenv, settc, setty, setvar, shift, source, stop, suspend,
switch, telltc, test, then, time, times, trap, true, type, ulimit, umask, unalias, uncomplete, unhash, unlimit, unset, unsetenv, until, wait, where, which, while – shell built-in commands
使い方例 (1) grepコマンド
早速ですが、このコマンドはどういう意味でしょうか?
$ grep -E 'warn|error' -i -A2 -B3 /var/log/install.log`
知っとるわ、という方には実につまらない話ですが、まあ一例なので少しお付き合いください。
まずは、 grep
コマンドが何なのかからおさらいしてみましょう。
名前
grep, egrep, fgrep - パターンにマッチする行を表示する
書式
grep [OPTIONS] PATTERN [FILE...]
grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
説明
grep は FILE で名前を指定されたファイルを検索して、与えられた PATTERN にマッチする部分を含む行を探します。 ファイルが指定されていない場合や、 ファイル名の代わりに 1 個のマイナス記号 “-”
が指定されている場合は、 grep は標準入力から検索します。 デフォルトでは、 grep はマッチした行を表示します。
さらに、兄弟プログラム egrep と fgrep は、それぞれ grep -E と grep -F と同じです。 これらの兄弟プログラムは非推奨ですが、後方互換性のために用意されています。
ふむふむ、ファイルの中からパターンにマッチした行を返却するコマンドなわけですね。 /var/log/install.log
の中からパターンにマッチする行を返すということがわかりました。
-E
オプション
-E
オプションは何でしょう?
正規表現の選択
-E, --extended-regexp
PATTERN を拡張正規表現 (ERE) として扱います (下記参照)。
拡張正規表現というのがあり、パターンを拡張正規表現として解釈するわけですね。
正規表現
正規表現とは、一群の文字列を一まとめにして表現するパターンのことです。 正規表現の構成方法は、数式によく似ています。すなわち、さまざまな演算子を
使い、小さな表現を組み合わせて構成するのです。
grep は、「基本」正規表現 (BRE)、「拡張」正規表現 (ERE)、「Perl の」正規表現 (PCRE) という 3 種類の正規表現文法を扱うことができます。 GNU grep
では、「基本」と「拡張」の文法の間で、利用できる機能に違いはありません。 他の実装では、基本正規表現は拡張正規表現ほど強力ではないものです。 ここでは、拡張正規表現について説明し、
基本正規表現との相違については、後で簡単にまとめることにします。 なお、Perl 互換の正規表現にはより多くの機能があり、 pcresyntax(3) や pcrepattern(3) で詳細に解説されていますが、 PCRE
が用意されているシステムでしか利用できません。
なるほど、正規表現にも複数種類があり、このマニュアル中のこの部分では基本的には「拡張正規表現」の方について書かれているとのことです。
その違いは、
基本正規表現と拡張正規表現
基本正規表現では、メタ文字 ?, +, {, |, (, ) は、その特殊な意味を失います。バックスラッシュを付けた \?, \+, \{, \|, \(, \) を代わりに使用してください。
ということがわかりました。今回の例の -E 'warn|error'
には |
が含まれているので、「基本正規表現」では無効となるようです。 -e
なら使ったことあったんだけど、という方、 -e 'warn|error'
だと実はうまく動かず、わざわざ -e 'warn\|error'
と書いてあげる必要があることがわかります。
この |
の意味は、
選択
2 つの正規表現は中置き型演算子の | で繋ぐことができます。 結果としてできあがる正規表現は、 どちらかの部分表現にマッチするどんな文字列にもマッチします。
と書かれています。
すなわち、 -E 'warn|error'
は、 warn
または error
という文字列を探すオプションだとわかりました。
-i
オプション
-i
オプションは何でしょうか?
-i, --ignore-case
PATTERN と入力ファイルの双方で、アルファベットの大文字と小文字を 区別しないようにします。
なるほど、パターンが大文字でも小文字でもマッチするんですね。つまり、ここでは error
のみならず、 ERROR
でも、はたまた ErRoR
でもマッチするというわけです。
-A
, -B
オプション
-A2 -B3
オプションは何でしょうか?
-A NUM, --after-context=NUM
NUM で指定した行数だけ、パターンにマッチした行の後に続く文脈も表示します。 マッチした行を含むグループ同士の間には、グループを区切る印 (--) からなる行を置きます。 -o や
--only-matching と同時に使うと、このオプションは効果がなく、警告メッセージを出します。
-B NUM, --before-context=NUM
NUM で指定した行数だけ、パターンにマッチした行に先行する文脈も表示します。 マッチした行を含むグループ同士の間には、グループを区切る印 (--) からなる行を置きます。 -o や
--only-matching と同時に使うと、このオプションは効果がなく、警告メッセージを出します。
なるほど、パターンにマッチした行の前や後の行を表示するオプションだということがわかりますね。今回は、 -A2 -B3
ですので、マッチした行の前2行、後ろ3行も表示するということのようです。それぞれ after/before の頭文字だったんですね。
実行
実際に上記のコマンドを実行したときの結果は次のようになりました。(わかりやすいように色がついていた部分に ** を加えています。)
2021-11-15 07:48:43-08 MacBook-Pro Installer Progress[51]: Progress app is loading…
2021-11-15 07:48:43-08 MacBook-Pro Installer Progress[51]: Progress app is running with no progress set
2021-11-15 07:48:43-08 MacBook-Pro Installer Progress[51]: Progress app is running…
2021-11-15 07:48:43-08 MacBook-Pro softwareupdated[295]: SUOSUServiceDaemon: **Error** reading /var/folders/zz/zyxvpxvq6csfxvn_n00000s0000068/C/com.apple.OSUpdate.SUOSUServiceDaemon.state: **Error** Domain=NSCocoaErrorDomain Code=260 "The file “com.apple.OSUpdate.SUOSUServiceDaemon.state” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/var/folders/zz/zyxvpxvq6csfxvn_n00000s0000068/C/com.apple.OSUpdate.SUOSUServiceDaemon.state, NSUnderlyingError=0x1387259a0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
2021-11-15 07:48:43-08 MacBook-Pro softwareupdated[295]: SUOSUServiceDaemon: Use standard 6 hour auto update interval
2021-11-15 07:48:43-08 MacBook-Pro softwareupdated[295]: authorizeWithEmptyAuthorizationForRights: Requesting provided rights: 1
--
}
2021-11-15 07:48:44-08 MacBook-Pro softwareupdated[295]: Adding client SUUpdateServiceClient pid=221, uid=0, installAuth=NO rights=(), transactions=0 (/usr/libexec/mobileassetd)
2021-11-15 07:48:44-08 MacBook-Pro Language Chooser[208]: Setup: Factory cable attached: 0
...
4行目に大文字小文字が混在した Error
がマッチしており、前後それぞれ3行と2行が取られているのがわかりますね。
使い方例 (2) findコマンド
次は別の趣旨の例を考えてみましょう。上記のgrepコマンドでは、既に用意されたオプションの正体を調べましたが、実際にはコマンドを書くときに、どのようなオプションをどう指定してあげるかを考えなければいけないケースが多いと思います。
今回はこれにならって、findコマンドで古いログファイルを探し出し、消したいとします。
"1日以上前に作られた、ファイルサイズが1MB以上のファイルを消したい" 場合のコマンドを書くとします。findを使い慣れていないとオプションの細部を思い出すのは難しいですよね。確か -mtime と -size というオプションがあったはずだったな、と思い出し、
$ find /var/log -mtime 24 -size 1M
こう書いたとします。
わかる人はわかると思いますが、間違っていますよね。3つの間違いが含まれています。manコマンドで調べてみましょう。
-mtime
オプション
-mtime n
ファイルの最終更新時間が、 n*24 時間前と比較して、 それよりも前か後かちょうど同じかをテストします。 何日前かを計算する際、 時間差を 24 時間で割った結果を丸めるため、
ファイル更新時間の解釈にあたって、 その処理がどのように影響するかについては、 -atime の説明を参照してください。
まず -mtime
の説明によると、このオプションは「時間」ではなく「何日前」かを指定するとありますね。前か後か、というのはどうやってテストするんでしょうか?
テストの項目を見ると、
テスト (test)
テストの中には、 -newerXY や -samefile のように、 その時点で対象となっているファイルと、 コマンドラインから指定する参照ファイル (reference file) との間で、 比較を行うものがあります。
そのようなテストが指定された際に、 参照ファイルに対する解釈は、 -H, -L, -P といったオプションや、 先行する -follow の存在によって決定します。 ただし、 参照ファイルが調べられるのは、
コマンドラインが解析されるときの 1 回だけです。 参照ファイルが調べられなかった場合 (たとえば、 stat(2) システムコールに失敗した場合)、 エラーメッセージが出力されて、 find は 0
以外のステータスを返して終了します。
数値引数である n は、 以下のテスト、 -amin, -mtime, -gid, -inum, -links, -size, -uid, -used において指定できます。 そして以下のような指定方法があります。
+n n より大きい。
-n n より小さい。
n n にちょうど等しい。
とあります。 -mtime 1
としてしまうと、 ちょうど 1日前に更新されたファイルでないといけないので、それより前を指定する場合は、 -mtime +1
と書く必要があることがわかりました。
-size
オプション
-size n[cwbkMG]
ファイルが n 単位分の容量と比較して、 それよりも小さいか大きいかちょうど同じかをテストします。 ここでは丸め処理が行われます。 以下のサフィックスが利用できます。
`b' 512 バイトブロックを表します (サフィックスの指定がない場合は、 これがデフォルトとなります)。
`c' バイトを表します。
`w' 2 バイトワードを表します。
`k キビバイトを表します (kibibyte, KiB, 1024 バイト単位)。
`M' メビバイトを表します (mebibyte, MiB, 1024 * 1024 単位 = 1048576 バイト)。
`G' ギビバイトを表します (gibibyte, GiB, 1024 * 1024 * 1024 単位 = 10737418241073741824)。
サイズというのは単純に、 システムコールの lstat (あるいは stat) に含まれている struct stat の メンバー st_size のことであり、 上に示すようにして切り上げられます。言い換えると、
その結果は ls -l で得られるものと一致します。-printf における `%k' と `%b' の書式指定子は、 スパースファイルに対して、 異なる扱いをする点に注意してください。 サフィックス `b' は、
常に 512 バイトブロックを表していて、 1024 バイトブロックを表すことはありません。 この点は、 -ls の動作とは異なるところです。
プレフィックスの + と - は、 ごく普通に、 それより上、 それより下、 を表します。 したがって、 n とちょうど同じ数には一致しません。 サイズとは、
次の数単位に向けて切り上げられることに注意してください。 つまり -size -1M というのは、 -size -1048576c と同じではありません。 前者は、 空のファイルにしか一致しません。 また後者は、
バイトサイズが 0 から 1,048,575 までのファイルに一致します。
-size
の方も同様に、1MB以上であれば、-size +1M
と書かなければいけないことがわかります。
また、このとき 1MB
は1メガバイト (1000 * 1000 bytes) ではなく、 1メビバイト (1024 * 1024 bytes)であることもわかりますね。今回はこの違いは問題ないと判断し、1メビバイト以上を削除することにします。
$ find /var/log -mtime +1 -size +1M
/var/log/powermanagement/2022.06.08.asl
/var/log/powermanagement/2022.06.09.asl
/var/log/powermanagement/2022.06.07.asl
/var/log/powermanagement/2022.06.06.asl
/var/log/powermanagement/2022.06.12.asl
/var/log/powermanagement/2022.06.04.asl
/var/log/powermanagement/2022.06.10.asl
/var/log/powermanagement/2022.06.11.asl
/var/log/powermanagement/2022.06.05.asl
/var/log/powermanagement/2022.06.01.asl
/var/log/powermanagement/2022.06.02.asl
/var/log/powermanagement/2022.06.03.asl
これでファイルリストが得られました。
-delete
オプション
削除するためには、 -exec
オプションで rm
をコールしてやることもできますが、それだけではなく、
-delete
ファイルまたはディレクトリを削除します。 削除に成功すると true を返します。 削除に失敗した場合は、 エラーメッセージを表示して、 ゼロ以外の終了ステータスを返します
(最終終了するときです)。
警告: find はコマンドラインを式として評価する点を忘れないでください。 したがって -delete を一番初めに記述すると、 それは find に対して、
検索開始ポイント以下のものをすべて削除させることになります。
コマンドラインから -delete オプションを指定すると、 自動的に -depth オプションがオンとなります。 一方で -depth は -prune を無効にするので、 -delete アクションは -prune
と同時に用いることはできません。
ユーザーの中ではよく、 コマンドライン上において -delete の前に -print を指定して、 削除が実際にどのように行われるかをテストしようとします。 予期しない結果を起こさないためにも、
そのようなテストを行うときから、 -depth オプションを明示的に指定することを忘れないでください。
-delete アクションは、 ディレクトリが空でない場合には削除に失敗します。
-ignore_readdir_race オプションとともに、 このオプションを指定し、 さらに、 親ディレクトリを読み込んだ後に、 対象ファイルが消失している状況が発生した場合、 -delete
アクションからのエラーは無視されます。 その場合には、 診断エラーは出力されず、 終了コードをゼロ以外には変えずに -deleteクションからの返り値も true になります。
-delete
オプションというのがあることもわかります。今回は /var/log 内なのでsudoを指定し、 -delete
オプションを付けてあげれば削除完了です。
まとめ
困った時は、Googleに頼らず man
コマンド。日本語版 manj
コマンドもうまく活用してみてくださいね。