web小説礼賛記

さらなる俺 TUEE をあなたに!

Vim入門 #1: どのVim?

※要改稿。 Vimmer はあまりに多かった

 にわか仕込みの知識で書いています。憶測も多い、いい加減な内容です。

なぜCLIエディタは流行らないのか

プログラミングに使う必要が無い

IDEが凄い(integrated development environment; 統合開発環境

 機能が凄すぎますし、CLI的な操作性の取り込みも進化しました。完全ではありませんが、プログラミングには十分な操作性です。

GUIエディタが凄い

 AtomVSCodeですね。初期状態でも非常に優れていますし、プラグインも容易に導入できます。学習コストがほぼ0なのに、CLIエディタの長所を上回る場面も多いです。

一般ユーザには必要無い操作性だった

 マウスもありますし、Ctrl や Option キーで単語単位の操作ができるなら十分、ということだと思います。

改善が遅かった

 Vimに埋め込みターミナル(:term)が付いたのは、2018年のことです。日本語入力の改善も、かなり遅かったと聞いています。

 CLIのエディタはプラグインが強いですが、逆に言えば、プラグインを入れなければ使い物にならない期間が長過ぎたのではないかと思います。

 今はかなり改善されましたが、それでも設定ファイルの編集は必須だと思います。

なぜCLIエディタは生き残っているのか

かつて、メインストリームだった

 シェルで見たように、「CLI=時代遅れの遺物」という印象は誤りです。歴史的には、GUIよりも、CLIエディタの方が高機能だったのではないでしょうか。

 良いGUIエディタが無かったとも言えそうです。

 lessコマンドの速さと高級さは、今でもGUIエディタより優れていますね。pagerのみならず、エディタも同様だったと想像できます。

手周りが良い

ターミナルの1タブとして使用できる

 小回りが利きます。

画面が良い

 実は、片大のCLIエディタはお洒落です。

機能的に優れている

フレームワークとして優れている

 CLIエディタ、特にVimは、独自のテキストの編集観を生み出しました。一度ハマったら抜けられません。

操作性が優れている

 マウス操作を使わずに思い通りの編集ができます。GUIエディタでは考えづらいことです。

 IDEやコードエディタにも、Vim風操作の拡張などがありますが、ディテールでは及びません。コード以外、つまり純粋なテキストの編集では、IDEよりもCLIエディタが優っています。

プラグインが強い

 手を突くせば、今でも最新の機能を持った環境に仕上げられます。特殊な修正を自在に施すこともできます。それを好んだ愛好家たちが、CLIエディタを支えています。

今でも必要性がある

日々の使用

 操作性の良さから、rcファイルの編集などには最適です。英語文書の編集にも適しています。文書を編集していく箱庭としても使えます。

 日本語の編集に使う余地もあります。

CLIエディタしか使えない状況

 具体的にはリモートログイン。サーバ周りの人などは、Vimを使わざるを得ないようです。

Vimの特徴

エディタとして

 操作の基本的な前提が変わります。

『モード』が分かれている

 通常は『ノーマルモード』で、カーソル移動やスクロールに特化しており、文字入力ができません。iキーで挿入モード。シフトキーの代わりに、Ctrl+vで選択モードに入ります。

 モード毎に操作が分かれているため、修飾キーを必要としないキー操作が多く、軽快なキーストロークで編集できます。

動作=操作+範囲

 まとまった操作単位で履歴がつくため、リピート操作や redo/undo が強力です。

Emacsとの比較

 Emacsはテキストデータの処理環境であり、Vimはテキストの編集操作環境だと思います。EMacsに詳しくないのでなんですが……。

日本語入力とVimの相性

 通常は、モードの切り替えは半角アルファベットで行います。そのため、頻繁に半角/全角を切り替えるか、自動的に半角/全角が切り替わるような設定を組みます。

  • 文節単位の操作ができます(非常に便利)
  • 句読点区切りの操作ができます(非常に便利)
  • スクロールは段落単位です(やや不便)
    • ピクセル単位ではありません
    • 表示行ではなく改行記号単位です

Vimの機能(箱庭として)

標準的な機能

マルチタブ :tabnew gt gT

 タブ操作には慣れが必要です。ファイルのエクスプローラもあります。

 [t]tのショートカットを設定するのが一般的です。

画面分割 :sp ^w

フォルダを開く

 プラグインの導入が必要です。 e.g. NERDTree

ウィンドウとバッファ b

 ウィンドウとファイルを自在に対応させることができます。たとえば、ウィンドウを閉じても、ファイルの編集状態はエディタ上に保持できます。

テキスト表示

ハイライト

 ソースコードやマークダウンなどの編集時に役立ちます。ファイルの種類に応じた設定も可能です。

文字数の常駐表示

 ステータスラインに表示する文字列を変更します。

特定字数で折り返し

 Goyoプラグインにより、一応可能です。生のVimは対応していません。

シェル

シェルコマンド :! command

 現ディレクトリ位置がありますし、シェルコマンドが使えます。

:terminal

 NeovimやVim8では、擬似ターミナルを起動できます。Vimの中でターミナルのタブを持てるようなものです。リモートログインで重宝するとか。

組み込みのヘルプ :help <anything>

 CLIツールの例に漏れず、詳しいヘルプが入っています。

どのVimを使う?

 どれを使ってもいいです。Spacemacs以外は大差無いと思います。

 本家Vimを使う場合は、アップデートしてあげてから使ってください。

本家Vim

動きが遅い

 特にiTerm2を使っていると、GVimGUI実装のVim)やNeovimの方が極端に速いです。

Neovim

UIを英語にする

 完璧な方法かはさておき、取り急ぎbash.rcにエイリアスを作ってしのぎます。これも中二病のためです。

nvim() { command nvim --cmd 'lang en_US' "$@"; }

.vimrc相当ファイル

 ~/.vimrcへのシンボリックリンクにしておきます。取り急ぎ。

$ mkdir -~/.config/nvim
$ ln -s ~/.vimrc ~/.config/nvim/init.vim

 本当はちゃんと管理した方がいいです。調査中。

GVim

操作性

 GUIアプリなので、マウス操作ができ、慣れ親しんだ類のショートカットが始めからます(ファイルを開く、タブ移動、など)。

 メニューバーからの操作が豊富です。初期状態では白背景だと思いますが、メニューから一発で変更できます。

Spacemacs

 というのもありかもしれません。設定が難しそう。

Atom or VS Code + Plugin

 細部が残念ですが、コーディングには十分だと思います。設定はGUIからになります。

入門法

 まず$ vimtutorをやります。チートシートを読んだり検索を繰り返し、メモを取っていくと良いと思います。

 まとまったテキストなら、やはりこの本です。キー操作に慣れてから読むと効きます。

PC環境の整備

データ管理

クラウド

 クラウドにデータを置けば、端末間で共有・同期できます。クラウドには履歴機能があるため、誤ってデータを消しても復元できます。

 e.g. Dropbox, iCloud, Google Drive, One Drive.

 Dropboxを使っておけば確実です。アプリを入れると同期が始まり、ローカル環境の$HOME/Dropbox/以下が、クラウドの内容に同期されます。

画面表示

なろうを縦書き&段組みで読む (PC)

 専用記事を書きました。

webページ毎に配色を変える

 上記の記事で導入したStylusで変更できます。

タイル型WM(ウィンドウマネジャ)

 導入法がやや玄人向きです。Windowsには良いアプリが無いかもしれません。

メリット/使用感

「画面が広く」なる

 複数の non-floating ウィンドウが、1画面に収まるように配列されます。ウィンドウが重ならなくなりますし、ウィンドウが一つだけなら、最大化して表示されます。実質的に、画面が広くなったのと同じ効果があります。

ショートカット操作

 フォーカスの移動、ウィンドウの微妙なリサイズ、位置の入れ替え、float化、最大化などができます。

 最大化しても、ウィンドウの位置関係が変わらないのが便利なところです。

chunkwmを導入する(Mac

 chunkwmは、Macで最も優れたタイル型WMです。キー入力と機能を結びつけるためには、他のソフトが必要です。

 導入には、こちらの記事などが参考になります。

 追記: 最近chunkwmの更新は終わり、yabaiが公開されました。

ブラウザ

比較

 FirefoxGoogle Chromeが安定しています。基本的な動作はChromeの方が軽いのですが、ツリー型タブを使えるのはFirefoxだけです。ただし、Firefoxにはメモリを大量に消費するという弱点(不具合?)があります。

 Vivaldiという新し目のブラウザもあります。Vivaldiは、画面分割が最も簡単なブラウザです。後発だけあって、デフォルトのUI機能がリッチだと思います。

 設定ファイルを書くのに慣れているなら、qutebrowserというのもアリです。縦型タブが使えます。

アドオン

ツリー型タブ (Firefox)

 オススメです。これのためだけにFirefoxを使っている人も一定数います。

導入

 ツリー型タブを導入し、サイドバー(右か左)にタブを表示します。このリンクを参考に、ブラウザ上部の箱型タブを消します。

 Firefoxのカスタマイズ機能で、タイトルをミニマムにするのもオススメです。

使用感

 まず、タブが縦に連なるため、無数のタブを開いても視界に収まります。さらに、タブをトピック毎にツリーで管理することができます。新規タブは、現在のタブの子となります。

Google検索で出てくるリンクを生リンクにする

 Google search link fixを導入します。Googleの検索結果のURLから、中間リンクを消してくれます。narou.rbでダウンロードするときなどに便利です。

Vimium

 キーボード操作を拡張するアドオンです。マウス不要でブラウジングできるようになります。

 ブックマークやタブを検索する機能もあります。

シェル入門 #5: 連番ファイルと目次生成

 やっと本編が始まりました。

事前知識

n個のデータと区切り文字

 シェルには、複雑なデータ構造はありません。標準入出力 (stdin, stdout) という単一の文字列を受け渡しして、コマンドによる処理を繋いでいきます。複数のデータを扱う場合、区切り文字 (delimiter, separator) を使用します。

# 改行区切りのデータ例
0 prologue.txt
1 go go go.txt
  • コマンドや引数は、シェルによって様々な文字で区切られます(単語分解; word splitting)。
  • コマンドには、標準入出力を改行区切りにして扱うものが多く、seq, ls, find, read, sort, sed などがそうです。
  • xargsは、単語分解とほぼ同様に、多くの区切り文字でstdinを分解します。データを表す文字列が空白を含む場合は、区切り文字を工夫して対処します。GNUのxargsなら楽ですが、BSD系(Mac)だと……。

その他

 #3に、マニュアルや履歴操作などのヒントが載っています。bashの文法について、詳しくは#4を読んでください。

先にまとめ

  • touch -a で新規ファイル生成
  • printf は、文字列のテンプレートとして使うことができる
  • xargs が便利だが、区切り文字と、パイプ時にはサブシェルに注意
  • while read は、特に複数ファイルに対する処理で有効
  • sedawk で、表文字列の加工ができる

New

 小説フォルダと連番ファイルを用意します。

test小説フォルダの作成

 ~/Desktop/test/を作り、test/src/test/build/ディレクトリを作ります。

$ cd ~/Desktop # change directory
$ mkdir test   # make directory
$ cd test
$ pwd          # print working directory
/Users/moba/Desktop/test
$ ls # list contents
$ # (何も無かった)
$ mkdir src build
$ ls -F # -F: classify (file, directory/, executable* などのように表示する)
build/  src/

 test/src/以下に文書を置き、任意のルビ文字や改行のスタイルに変換して、test/build/に出力する予定です。ルビ文字の自動補完も、次回実装します。

連番ファイル生成

 touchsrc/下に0.txt, 1.txt .. 24.txt を生成します。

 touchは引数のみを取り、標準入力を受け取りません。したがって、ファイル名は引数として与えます。

 以下では、touch -a を使います。-a を付けると、既存のファイルの『変更時間』を更新せず、ほぼ純粋な新規ファイルの生成コマンドになります。
 
 alias mk='touch -a'などとしてもいいかもしれませんね。

brace {} 展開

 bashの連番生成機能を使います。(c.f. #4

$ touch -a src/{0..24}.txt # 25個の引数を与える
$ # touch -a src/0.txt src/1.txt .. src 24.txt と等価
結果の確認
$ ls -FR # -R; recursively: 再帰的にディレクトリの中身をリストする
build/  src/

./build:

./src:
0.txt  11.txt 14.txt 17.txt 2.txt  22.txt 3.txt  6.txt  9.txt
1.txt  12.txt 15.txt 18.txt 20.txt 23.txt 4.txt  7.txt
10.txt 13.txt 16.txt 19.txt 21.txt 24.txt 5.txt  8.txt
$ # 出力は縦列に揃えられている。-xで横列に揃える

 失敗していたら、rm src/*もしくはrm -r srcで全消去できます。(ただし復元できません)。*を使用した表現はglobパタンと呼ばれ、シェルによってパス展開が行われます。

 ゴミ箱に捨てるtrashコマンドもあります。こちらは、インストールが必要かもしれません。

別解(連番ファイル生成)

 別解は飛ばしてもいいです。ただ、xargs -I は重宝します。

xargs -I <replacement-string>

 xargsは、標準入力をx個に分解し、後続のコマンドに引数として渡すコマンドです。-Iオプションによって、引数を参照できるようにします。

$ seq 0 1 # 改行区切りの連番を出力
0
1
$ seq 0 1 | xargs -I{} touch -a 'src/{}.txt'
$ # touch -a src/0.txt; touch -a src/1.txt; と等価
$ # xargs -I はtouchに引数を1つずつ与えた

 分解された引数毎に、{}文字を引数に置換しては後続のコマンドを実行しています。

echo

 echoでnからn.txtに文字列を変換してから、xargs touchを実行します。

$ seq 0 1 | xargs -I {} echo 'src/{}.txt'
src/0.txt
src/1.txt
$ seq 0 1 | xargs -I {} echo 'src/{}.txt' | xargs touch -a
$ # touch -a 0.txt 1.txt と等価
$ # xargs は引数を一度に大量に与えた
$ # (xargs -I と引数の渡し方が異なることに注意)

 出力を確認してから実行する、シェルらしい方法ですね。パスが空白を含む場合は、xargsへの入力の区切り文字を改行などに変えることで、適切に分解させる必要があります(方法は後述)。

sed(セド)Steam EDitor

 sedは、行ごとに置換などのアクションを適用します。置換には、正規表現が使用されます。

行頭^行末$に挿入(文頭・行末の'無'を置換)

 位置指定子^, $を使う方法があります。e.g. \.bak$は、拡張子.bakにマッチする。

$ # s/old/new/g: substitute 'old' with 'new' globally (=all, not once per line)
$ # / 以外の区切り文字を使ってもいい (今回は@)
$ seq 0 1 | sed 's@^@src/@g'
src/0
src/1
$ # 2つの置換を適用する( .. | sed | sed | .. でもいい)
$ seq 0 1 | sed -e 's@^@src/@g' -e 's/$/.txt/g'
src/0.txt
src/1.txt
$ # 最後に実際にファイルを作る
$ seq 0 1 | sed -e 's@^@src/@g' -e 's/$/.txt/g' | xargs touch -a
ampersand &

 .*で任意の文字列がマッチします。&文字で、マッチ部分全体を利用できます。sed以外の正規表現では、利用できないことあります。

$ seq 0 1 | sed 's@.*@src/&.txt@g'
src/0.txt
src/1.txt

 その他使った正規表現は、

記号 効果
. 任意の1文字(ワイルドカード
* 0回以上の繰り返し
後方参照 back reference (of a group)

 任意の文字列をマッチさせ、1つのグループとみなします。n番目のグループを、置換部分にて\nとして参照できます(n: 自然数)。

$ # BRE を使用 (basic regular expression)
$ seq 0 1 | sed  's/\(.*\)/\1.txt/g'
0.txt
1.txt
$ # ERE を使用 (extended regular expression)
$ seq 0 1 | sed -E 's/(.*)/\1.txt/g'
0.txt
1.txt

 正規表現の種類によって、エスケープなどが異なります。EREの方が、エスケープ無しでメタ文字になる場合が多いです。

コマンド 正規表現 意味
sedなど BRE basic regular expression
sedなど -E ERE extended regular expression
awk awk専用の機能が追加されたERE

awk(オーク)

 sedと同様に、行ごとにアクションを適用するコマンドです。レコード(行)中のn番目のフィールド(データ)を扱うことで、列に対する操作も表現できます。

$ # $n: n番目のフィールド (n=0 なら、全フィールド=レコード全体=行全体を表す)
$ seq 0 1 | awk '{print $0 ".txt"}'
0.txt
1.txt

 やや変わった記法ですが、レコード(行)毎にprint(f)が使えます。

 awkのprintはechoに近いです。引数の間に余白はありませんが、最後に改行が入ります。また、printfのようなフォーマット(後述)ができません。
 awkにはprintfもあります。

View

連番ファイルを番号順で確認

sort -n (natural sort)

 lsの出力は辞書順でソートされており、0.txt, 1.txt, 10.txt, 11.txt .. という並びになっています。

 代わりに、番号順にソートして表示します。

$ ls src | sort -n | column # -n: naturally / numerically
0.txt  4.txt  8.txt  12.txt 16.txt 20.txt 24.txt
1.txt  5.txt  9.txt  13.txt 17.txt 21.txt
2.txt  6.txt  10.txt 14.txt 18.txt 22.txt
3.txt  7.txt  11.txt 15.txt 19.txt 23.txt

 columnが出力を縦列に揃えてくれました。(-xで横列)

lsの出力について

 columnで整形しましたが、lsの出力は、パイプに通すと改行区切りになります。確認すると、

$ ls src | cat
0.txt
1.txt
10.txt
..

 cat; concatenate: 連結する(連結して出力する)
 入力が一つだけなら、単にそのまま表示することになります。

目次の生成

 通し番号とタイトルを列挙して、目次を作ります。ここでは、タイトルはファイルの一行目に書いてあるものとします。

 printfによるフォーマット(整形)を利用し、連番を右寄せにするのがミソです。タイトル(ファイルの一行目)は、head -n 1sed -n 1p <file>で切り出せます。

ファイルへの相対パス=ファイル名の場合

 ファイル名のソートが楽です。

$ pwd
/Users/moba/Desktop/test/src
while read f

 これは、stdinから一行ずつ切り出して、変数fに代入する書き方です。

$ # ※改行してコマンドを入力している部分は、頭に > がついている
$ find * | sort -n | while read f; do
>   printf '%2d %s\n' "$(basename "$f" .txt")" "$(sed -n 1p "$f")"
> done
 0 プロローグ
 1 急激な展開!
..
24 エピローグ
  • while read variable; do .. ; done がwhileループの記法です。
  • printf はテンプレートのように使用しました。
    • %2d: 整数(2桁右寄せ)
    • s%: 文字列
    • これらはフォーマット指定子と呼ばれます(c.f. man printf > Format)
  • basename は、拡張子を取り除いて番号を抽出しました。
  • sed でファイルの一行目(タイトル)を抽出しました。
  • 変数置換$fやコマンド置換$()については、#4を参照してください。
optional: basenameの代わりに変数展開${f%.txt}で.txtを消す [bash]

 これは$fの末尾の.txtを削除しますが、ファイル名以前には影響を与えないことに注意してください。例: a/b/c.txt -> a/b/c

別解: xargs sh -c

 ややこしいので、飛ばしてもいいです。また、shではなくてbashを使った方が無難です。

 まず、xargsを使った失敗を見ます。

$ find *.txt | sort -n | xargs -I{} printf '%02d %s\n' "$(basename {} .txt)" "$(sed -n 1p {})"
sed: {}: No such file or directory
printf: 0.txt: not completely converted
..

 sedの引数が、ファイル名ではなく{}となっていますね。コマンド置換$()が、xargs -I{}の実行(と、それに伴う{}文字の置換)よりも先に行われたためです。(コマンドの実行は、常に最後です。xargsが{}文字を置換しているのがポイントです)。

 xargs sh ..にすれば、まずxargsが{}文字を置換し、その後shコマンドの中でコマンド置換が行われます。

$ # sh は新たなシェルを起動する (bashでもいい。shは古いshell)
$ # sh -c は後続の文字列をコマンドとして実行する
$ find *.txt | sort -n | xargs -I{} sh -c 'printf "%02d %s\n" "$(basename {} .txt)" "$(sed -n 1p {})"'
xargsの区切り文字 delimiters of xargs

 man xargsによると、xargsは、タブ、EOF、改行、空白を区切り文字として扱います。従って、xargsに渡すファイル名がスペースを含む場合は、区切り文字を限定する必要があります

 GNU xargs なら、xargs -d '\n'で改行を区切り文字にできます。

 MacBSDUNIX)なら、xargs -0によって、区切り文字をヌル文字\0にします。ヌル文字区切りのデータをxargsに渡せば、意図通り動きます。(例: find .. -print0 | sort -zn | xargs -0)。

 MacHomebrewを入れている場合、brew install findutilsで、gxargsとしてGNU xargsを利用できるようになります。

相対パス≠ファイル名の場合

 src/0.txtなどとなっているケースを扱います。この場合、ソートが困難です。

目次を作ってから、後でソートする

 目次の頭に通し番号があるため、最後にソートしてしまえという戦法です。

$ find src/* | while read f; do
>   printf '%2d %s\n' "$(basename "$f" .txt)" "$(sed -n 1p "$f")";
> done | sort -n
sort -V

  prefix version suffix という形式の文字列をソートするオプションです。この方法は、ディレクトリ名が数値を含む場合は無効です。

sort -n -t -k

 tでフィールド(列)のseparatorを指定します。-kで、どのフィールドを基準にしてソートするかを決めます。

$ find src/* | sort -n -t '/' -k 2
src/0.txt
src/1.txt
src/2.txt
..

 ファイル名が番号から始まるため、上手くソートできました。ただ、こんなオプションはmobaには覚えられません。

 一般的に最後のフィールドを指定する方法? 要改稿

Edit

 さらに行きます。

zero-paddingの連番(必要なら)

 ファイル名を00.txt, 01.txt .. にします。辞書順のソートでも番号順にソートされるのが利点です。

 このシリーズでは、0.txt, 1.txt .. のままにしますが、方法だけ紹介します。
 ちなみに、lsの代わりにexaを使えば(#6)、辞書順ではなく数値の大小でソートされるため、それで解決とするのがクレバーだと思います。

生成

 そもそも、seqにzero-paddingのオプションがあります。

$ seq -w 0 24
00
01
..
$ seq -f %03g 0 24
000
001
..

 しかし、暗記が困難です。mobaならprintfを使います。

$ seq 0 24 | xargs printf "%02d\n"
00
01
..
24

リネーム (0.txt, 10.txt -> 00.txt, 10.txt)

 失敗すると原稿データが危険なため、test/tempで実験し、test/temp/convertedにリネームしたファイルを作ります。(temp: temporary / 一時的な)

$ pwd
/Users/moba/Desktop/test
$ mkdir -p temp/converted # -p; parent: 親ディレクトリが無ければ生成する
$ cp src/*.txt temp
$ cd temp
$ ls -F
0.txt      13.txt     18.txt     22.txt     5.txt      converted/
1.txt      14.txt     19.txt     23.txt     6.txt
10.txt     15.txt     2.txt      24.txt     7.txt
11.txt     16.txt     20.txt     3.txt      8.txt
12.txt     17.txt     21.txt     4.txt      9.txt

 リネームを実行します。

$ find *.txt | while read f; do
>   printf '%02d.txt\n' "$(basename "$f" .txt)"; 
> done
00.txt
01.txt
..
$ find *.txt | while read f; do
> printf '%02d.txt\n' "$(basename "$f" .txt)"
> done | xargs -I{} cp "$f" 'converted/{}'

 find .. | xargs ..も使えますが、やはりxargsに与えるコマンドがsh -cである必要があり、長くなりがちです。

 find .. -exec \; を使うこともできます。

結果の確認
$ ls -FR
0.txt      13.txt     18.txt     22.txt     5.txt      converted/
1.txt      14.txt     19.txt     23.txt     6.txt
10.txt     15.txt     2.txt      24.txt     7.txt
11.txt     16.txt     20.txt     3.txt      8.txt
12.txt     17.txt     21.txt     4.txt      9.txt

./converted:
00.txt 03.txt 06.txt 09.txt 12.txt 15.txt 18.txt 21.txt 24.txt
01.txt 04.txt 07.txt 10.txt 13.txt 16.txt 19.txt 22.txt
02.txt 05.txt 08.txt 11.txt 14.txt 17.txt 20.txt 23.txt

 なお、zero-paddingの解除(zero suppression)には、printf '%d' 数値が使えます。| sed 's/^0*//g みたいなのもいいかもしれません。

シェル入門 #4: bashの基礎知識

 第四弾。主にbashの文法を扱います。辞書的な項なので、一気に読む必要はありません。まずは新しいLinuxの教科書などを読むのを勧めたいです。

シェルとは

 主にコマンドラインインタープリタです。ターミナル経由でアクセスします。

 シェルに文字列を渡すと、解釈・整形してからコマンドに渡してくれます。コマンド(外部コマンド)自体は、どのシェルを使っても同じものを呼び出しています(cdなど、シェルのソースに組み込まれたコマンドを除きます)。

$ touch -a test.txt # ファイルを生成
$ ls # list: 現ディレクトリのコンテンツを表示
test.txt

 man bashGNUのページに文法が載っています。とことんbashに詳しくなりたいときに読むと良いでしょう。

シェルの種類

 このシリーズでは、bashという標準的なシェル使用します。bashはwebの情報量が多いため、スクリプトを書くのが(比較的には)簡単だからです。bashの古い部分に振り回されることになりますが、仕組みを理解すれば、あまり問題ありません。

 bashに慣れてきたら、普段使いのシェルは変更してもいいかもしれません。スクリプトだけbashで書けばいいので。

 最近のMacのデフォルトのシェルは、zshです。今でこそbashthe shell という位置付けですが、環境は今後変わるかもしれません。

スクリプトとワンライナ

 一行で書き捨てるようなコードは、ワンライナ(one liner program)と呼びます。シェルスクリプトとは、複数行のシェルコマンドを書き連ねたファイルです。

 昔のGitはシェルスクリプトで書かれていたらしいですよ。

 mobaの場合、普段はfishシェルでワンライナを書き、スクリプトを書くときは、bashプログラミング言語を使います。

シェルの移行について

 シェルの乗り換えは難しくありません。どのUNIXシェルも文字列ベースで、しかも外部コマンド(ls, find, grep, sed, ..)を呼び出すことになるため、作業のコア部分は変わらないからです。

 ただし、スクリプトを新しいシェルで書けるようになるには、相応の時間が必要です。なぜなら、細かい一つ一つの機能が重要になるからです。

 乗り換え先のシェルで設定ファイルを書けるようになれば、普段使いには困らないでしょう。最も親切なfishシェルをオススメしておきます。

使用中のシェルの確認

 Mac / Linux 使いの人は、初期状態でbashを使っていると思います。最近は分かりませんが。

$ echo $SHELL # シェルを確認。普通、環境変数SHELLに書いてある
/bin/bash

 学校のPCのシェルがcshだった! という方は、その、お気の毒ですね。cshは、今日ではガラパゴスです。bashコマンドを実行してbashに入り、source ~/.bash_profileとすれば、一応bashが使えます。

Windowsについて

 Windows版のbashもありますが、余裕があれば、Linuxの仮想環境を作った方が無難だと思います。(WSLは、色々足りないものがあったので、初見ではお勧めできません)。

 Powershellを使うなら、Windowsもありだと思います。ただ、UNIXシェルとは全く毛色が違うため、このシリーズは役に立たないでしょう。

基本的なbashの解釈 Basic interruption by bash

 文法の説明に入ります。どのシェルでも似たようなことができますが、ディテールはシェルによるでしょう。

コメント#

$ echo a # シャープ記号以降は無視される
a

改行して続行 \

$ echo \
> a
a

 二行目の>文字は、環境変数$PS2の値で設定されます(PS2: secondary Prompt String)。

逐次実行 ;

 これは『リスト』の一種です。

$ echo a; echo b
a
b
$ echo a echo b # 2つめのechoという単語は、1つめのechoの引数
a echo b

引数の分解

 コマンドへの引数は、bashによってスペース区切りで分解されます。

$ echo a b
a b
$ echo a    b
a b
$ # echoは、2つの引数を空白区切りで連結して出力した

 スペースを引数として渡すには、クオーツ'"エスケープ\を使う必要があります(後述)。

コマンドの入出力

 引数/オプション、stdin/stdout、return/exit status. それらがコマンドの入出力です。

コマンド、引数、オプション commands, arguments, and options

 雰囲気で理解してください。オプションが引数を持つこともあります。

$ # ファイルを読み、3行目を出力する
$ cat text_file.txt | sed -n 3p
$ # sedコマンドは理解してなくてもいいかも

 オプションの書式はGNU規格が良いのですが、古いコマンドは古い規格に則っています。

サブコマンド

 コマンドの中にコマンドがある、という場合です。いずれ、このシリーズでその手のスクリプトを書きます。

$ # git のサブコマンド status を
$ # --short オプション付きで呼び出す
$ git status --short

 bashの文法的には、上記statusは通常の引数に過ぎません。gitコマンドの内部で、サブコマンドとして処理が分岐します。

標準入出力 stdin/stdout: standard input/output

 ノリで理解してください。なお、標準出力は、消費しない限り書き足されていきます。リスト(後述)単位で出力内容が共有されると思っていいでしょう。

$ echo a; echo b
a
b
$ echo a | echo b # パイプ`|`が標準出力を消費し、次のコマンドのstdinにする
b
$ # (続くechoはstdinを使用しなかった)

 標準入出力というのは、プロセスへの配列引数の0番と1番です。標準エラー出力(後述)は2番となります。

標準エラー出力 stderr

 実は、通常の出力1とエラー出力2は分かれており、上手く扱うと便利なときもあります(後述)。

終了ステータス Exit Status

 コマンドの出力は3つあり、stdout, stderr、そして『終了ステータス』です。直前のコマンドの終了ステータスは、?変数として参照できます。

$ echo a
a
$ echo "$?"
0
$ # 0は成功、その他は失敗を表す
e.g. ファイルが存在すれば実行

 []はテストコマンドといいます(man [を参照)。記事の終わりの方でも解説します。

$ # ファイルが存在するかを調べる
$ [ -f ~/.bashrc ]
$ echo $?
0
$ # 0: 成功; ファイルが存在した

 テストコマンドの終了ステータスを利用して、ファイルが存在すれば読み込むことをやって来ます。

$ # ~/.bashrc というファイルが存在すれば、読み込む
$ if [ -f ~/.bashrc ] ; then # 改行は任意
    source ~/.bashrc
fi
$ # こうとも書ける:
$ [ -f ~/.bashrc ] && source ~/.bashrc

Redirection

 コマンドの入出力先を切り替えます。

  • direction: 方向
  • redirection: 方向切り替え

T字で考える

 主な切り替えは、以下のように可視化できます。tee: T字, cat: concatenate; 連結して表示

tee       cat      > (redirection)
+--+-→   +--+-→  +--+
   |         |        |
   v         +        v

where
  stdin +--+--+ stdout
           |
           +
          file

 基本的には、入出力は標準入出力しかありません。

pipe/pipeline commandA | commandB

stdout → stdin

 前のコマンドの標準出力(stdout)を、次のコマンドの標準入力(stdin)にします。出力先をターミナルから次コマンドに切り替えるものであり、リダイレクションの一種です。

$ echo あああ | less # echo による出力「あああ」をlessに渡す

 標準入力を加工して標準出力に出すコマンドを、特にフィルタと呼びます。(古い言葉ですが)。複数回フィルタを適用してデータを加工するのがシェルの基本です。

Note: subshell

 パイプ先のコマンドは、subshellという名前のサブプロセスで実行されます。このため、パイプ先のコマンドで変数を書き換えたり、cdしても、元のシェルに影響を与えないケースが出て来ます。(#8で例が出ます)。

読み書き > >> <

 ファイルとコマンドのstdin/stdoutを繋ぐリダイレクション(入出力先の指示)です。

記号一覧

記法 効果
command > file ファイルを空にして書き込み
command >> file ファイルの末尾に追記
command < file ファイルの内容をstdinに読み込み

 データの流れが分かりやすいため、command < fileよりもcat file | command..を使うことが多いです。パイプ|先のコマンドはsubshellで実行されるため、やむなく<を使うことはあります。

 シェルの状態(変数など)を変更する目的では、パイプ先のコマンドは無効です。

> /dev/null(デブヌル)

 devとはdeviceを表しています。/dev/nullは、出力を捨てるための仮想的なアドレスです。#3下部にて、pushdの出力を捨てるために使用ました。ログを破棄することで、高速化に活かせる場合もあります。

ファイルの内容を加工して上書き

 cat file | .. > fileは禁忌です。cat fileの実行前に、>に備えてfileが空になります。こういう事故があり得るので、シェルを使うなら、事前にバックアップを取っておくのが基本となります。

 バックアップに有効なのは、以下のような操作です。

$ # a)
$ # まず加工結果を一時ファイルに収める
$ cat file | (加工作業) > file.new
$ # 元ファイルを一時ファイルで上書きする
$ mv file{.new,} # {..} の部分は "brace展開"(後述)
$ # ↑はmv file.new file と同じ

$ # b)
$ # 強引に加工結果を元ファイルに流す
$ cat file | (加工作業) | tee file > /dev/null
$ # teeがstdinをfileと/dev/null両方に出力した

$ # c)
$ # そもそも元ファイルを上書きできる加工コマンドを使う
$ gsed -i (加工指示) file # GNU sed

n< file n> file m>&n

 飛ばしていいです。0番はstdin、1番はstdout、2番はstderrを表します。

e.g. 入出力
$ cat 0< stdin.txt 1> stdout.txt 2> stderr.txt
$ echo 'error_message' 1>&2
$ ls 1> stdout_and_stderr.txt 2>1&

 &は出力を束ねます。1>>と等価で、0<<と等価です。3行目のlsは、リダイレクションを書く順番が重要になります。(1>2&は無効)

このリダイレクションはどの位置に書いても良い

 1>&2 echo 'error_message'などの記法もあり得ます。

e.g. error用のシェル関数

 スクリプトファイルを書くときに役立ちます。

_err() {
  printf '%s\n' "${1}" 1>&2
}

here documents << word_to_finish

 入力した文字列を、stdinとしてコマンドに注入できます。改行を含む場合も、視覚的に読みやすく書けるのが便利です。

$ cat <<FIN >> ~/.bashrc
> # あああ
> # いい
> FIN
$ # 途中で書くのに失敗しても、Ctrl+c で中断できる

 <<-ならタブ文字でインデントして書ける、など、詳しい話はman bashを参照してください。

here strings <<< "$var" [bash]

 変数の内容をstdinとして注入できます。bash依存の機能です。まあ覚えなくていいのではないでしょうか。

展開と順序

単語分解 word splitting

 後述の変数展開やコマンド置換、算術式展開の後に行われます。デフォルトでは、スペース、タブ、改行で分解されます。

 重要なのは、展開結果をbashにバラされたくなければ、"${var}"のようにクオーツで囲む必要があるということです。

環境変数IFS

 IFS: internal field separator は、bash組み込みのreadコマンドと、単語分解で使用されます。詳しくは、man bashを読むか、別の機会で。

設定する場合は、エスケープ文字のために$記号を忘れないように注意しましょう。

パス展開 Pathname Expansion

 bashが引数を展開(置換)します。そのため、任意のコマンドに使えます。

~ tilde

$ echo ~ # ホームディレクトリ($HOME)に置き換えられている
/Users/moba

 単純な置換であり、実はパス展開と区別されます。実行タイミングも異なりますし。

* wildcard

 任意のファイル名などに使えます。これは広く標準的な機能で、globパタンとして知られています。

$ echo *
(現ディレクトリのコンテンツ一覧が出力される)
$ echo *.txt
(現ディレクトリの.txtファイル一覧が出力される)
$ # 直下の.txtファイルを列挙する
$ for f in *.txt ; do echo "$f" ; done

 パス展開は、マッチするものが無ければ展開されず、*文字のままになります(bashの場合)。

エスケープ Escapes

 デフォルトの挙動をエスケープして(避けて)、別の意味を表す機能です。たとえば空白を、引数の区切りではなく、通常の空白文字として扱えます。

エスケープ一覧

記号 効果・解説
\ 直後の一文字をエスケープ
' 中身を一つの単語として扱う 中身の変数展開は無し
" 中身は変数展開${var}のみ実行される
String-based

 なお、bashにおいて、値とは(基本的に)文字列であり、3'3'の解釈結果は等しく文字です。クオーツはエスケープであり、文字列型を作るためのものではありません。

バックスラッシュ\ backslash

$ echo \\     # 改行して続行、をエスケープ
\
$ echo \~ \*  # チルダ展開とパス展開をエスケープ
~ *
$ echo a\ \ b # 空白をエスケープ(→ 1つの引数)
a  b
$ $ echo 'a  '\''  b' # シングルクオートをエスケープ(空白文字を含む1つの引数になる)
a  '  b

クォーテーション '" single/dobule quotes (quotations)

$ echo a         b # 2つの引数
a b
$ echo 'a    b' # 1つの引数
a    b

展開・置換

変数展開 $var ${var} Variable expansion

 変数展開の後に単語分解が実行されることに注意してください(bash)。

$ test='a    b c' # 変数を定義
$ echo test
test
$ echo $test   # 展開 → 単語分解(3つの引数)
a b c
$ echo "$test" # 展開(1つの引数)
a    b c
$ echo '$test' # 展開されない
$test

 "${filename%.*}"で拡張子を取り除くなど、様々な機能が付随しているようです。

 シェルに依存した機能なので、あまり汎用性はありません。しかし、シェルスクリプトを書くときには重宝します。そのときにまた触れます。

brace{}展開 Brace expansion

 表現される全パタンとして展開(置換)されます。

$ # 連番生成はbash特有の機能なため注意
$ echo {0..3}.txt # 4つの引数
0.txt 1.txt 2.txt 3.txt
$ echo {a,b}{c,d} # 4つの引数
cc bc ad bd
{}""の中では展開されない

 ""の中で展開されるのは、"${variable}""$(command)"のみです。

$ echo "{0..3}.txt" # 展開されない
{0..3}.txt
$ echo '{0..3}.txt' # 展開されない
{0..3}.txt
e.g. バックアップ
$ # バックアップファイルを作る
$ cp somefile{,.bak}
$ # cp somefile somefile.bak と等価

$ # バックアップで元ファイルを上書きする
$ mv somefile{.bak,}
$ # mv somefile.bak somefile と等価

算術式展開 $((..)) Arithmetic Expression

 式の計算結果に置換されます。exprコマンドもありますが、速さなどの面から、$((..))の方が良いそうです。

$ echo $((1 + 2*3)) # 式中のスペースも許される
7
e.g. 10分後に同名の全アプリを閉じる
$ # &でバックグラウンド実行
$ (sleep $((60*10)) && killall 'AppName') &
$ # (ただし、シェルが終了したら中断されるので不便)

コマンド置換 $(..) Command Substitution

 コマンドの実行結果に置換されます。bashの場合、展開結果も単語分解されるため、"$()"と書くのが無難です。

 $()の代わりに`command`とも書けますが、入れ子に弱いので非推奨です。

$ echo ls
ls
$ echo `ls` # 非推奨
0.txt 1.txt 2.txt 3.txt
$ echo "$(ls)" # 推奨(入れ子に強い)
0.txt 1.txt 2.txt 3.txt
$ # catのコマンド置換により、stdinを引数としてechoに渡す
$ ls | echo "$(cat)"
0.txt 1.txt 2.txt 3.txt
$ # シェルによっては永遠に停止しますが
$ # Ctrl+Cでコマンドを中断できます

 通常は、コマンド置換よりもパイプを好みます。データ加工の流れが分かりやすいためです。パイプは並列的に実行されるため、パフォーマンスが良い場合もあります。

e.g. findで見つけたディレクトリへ移動

 cd "$(find ..)"というをたまにやります。find .. | cd "$(cat)"は、パイプ先のcdがサブシェルで実行されるために、元のシェルのディレクトリ位置が変わらず、無効です。

 なお、cdに渡したパスが着色されている(ANSIカラーコードを含む)とcdに失敗します。エラー出力を読んでも、失敗した理由が分からないので気をつけてください。(エラー出力は通常の文字色に見えるため)。

プロセス置換 <() process substitution [bash]

 普通使いません。コマンドの出力を、stdinとして与えることができます。

$ cat < <(seq 1 2)
1
2

展開の順序 (bash)

 左から右の順で実行されます。クオーツの展開は、単語分解の直前に行われます。

{} ~ (${} $() $(())) 単語分解 *などパス展開

 静的表現の展開 → 式の評価・展開 → 単語分解 → パスの補完 という印象でしょうか。最後にコマンドを実行します。

プロセス Processes

 オタク度が増してきました。

subshell

 パイプ先のコマンドは、sub shellという名の別プロセスで実行されます。シェルスクリプトも同様です。

 別プロセス(サブシェル)でcdやexitをしても、呼び出し元のプロセス(シェル)には影響を与えません。したがって、環境変数の書き換えや、呼び出し元の変数、シェル関数の定義などにも影響しません。

listが実行されるシェル

 シェル関数は、{}で定義した場合、現在のシェルで実行されます。従って、{}の中でcdexitを使った場合、呼び出し元のシェルに影響を与えます。

連結/分岐 Sequences / Conditionals

 すべて man bash > SHELL GRAMMAR に載っている内容です。

リスト list

 ;&&&||などで繋がれる、一連のコマンドです。

リスト全体が出力を共有する

 printf a; printf b;の出力はabです。

OR list commandA && commandB

 commandAの終了ステータス($?)が0(成功)ならば、commandBを実行します。

AND list commandA || commandB

 commandAの終了ステータス($?)が0以外(失敗)ならば、commandBを実行します。

複合コマンド Compaund Commands

(list) サブシェル生成

 listをサブシェルで実行します。バックグラウンド実行するときなどに便利です。

{list} group command

 listを現在のシェルで実行します。

func() compound-command

 シェル関数を定義します。func() { list }とし、現在のシェルで実行するのが基本です。

((expr)) [bash]

 ぶっちゃけ使いません。

解説

 expr != 0 という変換をします。つまり、(())の内部を算術式展開と同様に評価し、その値が0以外なら0、0なら1を返します。

 さらに、特別な構文として、C言語風のforループがあります。

$ for ((i=0; i<=100; ++i)) do ..

 が、for i in "$(seq 0 100)" do ..で良いと思います。

テストコマンド[] [[]]

 man [が重宝します。

終了ステータス exit status / return status

 前述の通り、コマンドはstdoutの他に終了ステータスを返します(0で成功、他は失敗を表す)。

c.f. man [

 テストコマンド[]は、中の式を評価して、終了ステータスで真偽を返します。0が真、1が偽という捉え方をします。

テストコマンドのオプション抜粋
コマンド 効果
-e ファイル or ディレクトリが存在するか?
-f ファイルが存在するか?
== 文字列の比較
-eq 数値の比較

[[ expr ]]bash専用)

 より強力な[]です。man bashに載っています。

 正規表現との比較left =~ rightが使えます。

  • 正規表現部分をクオーツで囲うとエラーになります
  • マッチする部分を含むなら真であるため、完全一致には^pattern$を使います
  • 『X = A or B』の判定も簡単にできます

 ニッチな所としては、

  • -aの代わりに&&で論理積を書けます
  • -oの代わりに||で論理積を書けます

 mobaは、そんな知識を増やすよりもLL(軽量言語)を使います

case

 引数のパースで重宝します。また#7などで扱うと思います。

case "${cmd}" in
    'h' | 'help' | '-h' | '--help') _cmd_help "$@" ;;
    'l' | 'ls' | 'list') _cmd_list "$@" ;;
    'debug') "$@" ;;
esac

シェル入門 #3: 初見(マニュアル、操作法、予備知識、bashrc)

 第三弾では、シェルが初見の人に必要な知識を扱います。ただ、まずは新しいLinuxの教科書を読むべきだと思います。$文字が何を表すか分らなければ、尚更のことです。

 シェルはbashの前提で行きます。最近のMacはデフォルトでzshを入れているので、時代は変わるようですが……。

マニュアルなど

tldr

 tldrは、非公式ですが最速のマニュアルです。コマンドの概略と、いくつかの例を見せてくれます。インストールしておくと良いでしょう。

 新しいコマンドを見たときは、まずこれを引けば良いと思います。

man

 manは、コマンドの公式マニュアルをwebから取ってきて表示するコマンドです。しばしば詳しすぎるため、参照性は低いと思います。

 man bashでは、bashシェルの文法が見られます。

日本語版のmanを入れる

 方法? 要改稿。

less

 manは、ページャ(閲覧用ソフト)としてlessを使用しています。重要な操作としては、

キー 意味 解説/備考
q quit 閉じます
h help 操作法が見られます
/ search n、Nで前後を検索
& search all 検索パタンを含む行を抽出します

 &検索を終了するには、再び&キーを押してから、Enterとすればいいです。

その他ヘルプの表示法

<command> --help

 引数やオプションのリストが見られます。

help <built-in command>

 built-inコマンド(シェルのソースに実装が書いてあるコマンド)のマニュアルです。--helpを渡した時と同じ表示が出ます。

操作法

 ターミナル上のシェルの操作について。

補完 TAB

 TABキーで、パスやコマンドの『補完』(自動入力)がされます。候補が複数ある場合は、補完は実行されません。(候補は、現在の入力内容によって絞られます)。二度押せば、すべての候補が表示されます。

履歴/コマンドの停止

 特に停止は必須です。<C-x>はCtrl+xを意味します。

操作 機能
↑↓ コマンド履歴の移動
<C-r> 履歴の検索
<C-c> コマンドの停止

 なお、<C-z>で実行中のプロセスを停止し、fgコマンドで戻ってくることもできます。less、man、Vimなどで使えます。

historyコマンド

 履歴番号とコマンドの内容を列挙します。!<履歴番号>で、過去に実行したコマンドを再実行できます。

ショートカット/操作性

 Macでは、EMacsと似たショートカットが設定されています。他の環境でも、set -o emacsを実行すれば使えるはずです。

操作 機能 対応 機能
C-a 行頭へ C-e end 行末へ
C-f forward 右へ C-b backward 左へ
C-d delete C-h backspace
C-k 行末まで削除
C-n next line 次の履歴 C-p previous line 前の履歴

 実は、set -o viとすると、vi(エディタ名)の操作性を利用できます。vimではないため、visual modeは利用できませんけれど。

 setコマンドの実行は、後述の.bash_profileに書けば、自動化できます。

予備知識

表記法

プロンプト$(もしくは#

 ユーザの入力待ちを意味する文字です。$はユーザ、#はルートアカウントを表します。プロンプトは、ユーザ名やディレクトリ位置を含むことが多いですが、このブログでは、常に省略して$と表します。

高級なプロンプト

 パスとコマンドで行を分けたり(二行プロンプト)、コマンドの入力後に改行が入るような表示もあります。時刻や作業状況を表示するものもあります。

 配布されているものは、試すのが楽でしょう。

パス

記号 意味
~/ ユーザのホームディレクトリ($HOMEと同じ)
./ 現在のディレクト
../ 1つ上の階層(ディレクトリ)
/ ルートディレクト

 ディレクトリ名の後にスラッシュを入れるかは任意です(これは一般的なPOSIX規格)。

隠しファイル

 .から名前が始まるファイルは、隠しファイルとして扱われます。

bashのsetコマンド

 bashの設定は、環境変数エイリアス、シェル関数、そしてsetコマンドで行います。

 setは組み込みのコマンドで、色々できます。特にスクリプトを書くときに役立ちます。

bashの設定ファイル(飛ばしてもいいです)

 存在しない場合は、自分で作って保存します。GUIのエディタを使っても良いですし、Vimなど、CLIのエディタに慣れてからでも構いません。

予備知識

rcファイルとは

 run commandsファイルの略です。単にシェルコマンドのリストと思って良いでしょう。

 書き換えたら、source ~/.bashrcなどとして、その場で反映できます。(~/.bashrcが現在のシェルで実行されることで、設定が書き換わります)。

 ~/.bashrcの設定によく使う機能を紹介します。

エイリアス(別名を付ける)

 alias name='words'

シェル関数の定義

 function() { .. ; }(現在のシェルで実行されるシェル関数)

分割ファイルしたrcファイルの読み込み

 source <file>

bashのrcファイル

~/.bash_profile

 このファイルのコマンドは、シェルへの『ログイン』時に実行されます。詳しくは、man bash > INVOCATION に乗っています。

 export環境変数を設定する場合、~/.bashrcに書きます。

# ~/.basrc を『ログイン』時にも実行する(重要)
source ~/.bashrc

# プロンプト文字を <ディレクトリ名> $ と言う形式にする
# 文字色0;32mは黄緑色
export PS1='\[\e[0;32m\]\w \[\e[0m\]\$ '

# 重複するコマンド履歴を自動削除(上キーで履歴を遡るのが便利になる)
export HISTCONTROL=ignoredups
プロンプト文字の設定方

 ANSIカラーコードに基づいて着色できます。配布されているプロンプト文字を利用するのも良いでしょう。

~/.bashrc

 対話モードの非ログインシェルを起動する度に読み込まれます。上記の追記により、ログイン時(ターミナルの起動時)にも読み込まれるようになりました。

 エイリアスやシェル関数をここに書きます。好みで設定してください。dotfilesで検索すれば、他の人の設定も見られます。

# ~/.bashrc

# "$n": n番目の引数の内容
# "$@": 全ての引数

# command <some_command>: シェル関数を無視してコマンドを呼ぶ (詳しくはhelp commandを参照)
# 無限ループを避ける場合などで有効

##### man #####
# pagerを変更(manが使用する環境変数を設定する方法もある。詳しくは: man man)
man() { command man -P 'command less -iNMR' "$@"; }
# マニュアルの見出しを手に入れる
man-table() { man "$@" | grep -v '^\s'; }

##### prompt string  #####
# n: normal, s: short
ps-n() { export PS1='\[\e[0;32m\]\w \[\e[0m\]\$ '; }
ps-s() { export PS1='\$ '; }

##### source #####
alias src='source'
src-b() { source ~/.bash_profile; }
src-v() { source ~/.vimrc; }
alias v='vim'
v-src() { vim "$1"; source "$1"; }
v-bash() { vim-src ~/.bashrc; }

##### cd を pushd にすり替える #####
cd() { command pushd "$@" > /dev/null; } # pushdの出力は捨てる
pd() { command popd "$@"; } # pd: pop directory でディレクトリ位置を遡る
cdh() { command dirs "$@"; } # ディレクトリ移動の履歴を表示
mkcd() { mkdir -p "$@" && cd "$@"; }
.() { cd ../"$1"; }
..() { cd ../../"$1" }
...() { cd ../../../"$1"; }
....() { cd ../../../../"$1" }
.....() { cd ../../../../../"$1"; }

#### 閲覧用 #####
 ls -F: classify (file, directory/, symblic_link@, executable*, のようになる)
 ls -G: enable colorized output
s() { command ls -FG "$@"; } # ls: list
ll() { command ls -FGAlh "$@" ; } # ll: list in long output
# list directories
lsd() { command ls -FGA "$@" | grep '/' | column; } # columnで複数列に整形
# list naturally (numerically)
lsn() { command ls "$@" | sort -n | column; }
# tree -N: 日本語対応 -C: 着色 (colorized)
tree() { command tree -NC "$@"; }
# less -R: 着色対応、-N: 行番号表示、-M: スクロール位置表示、-i: --ignora-case; 検索時に小文字を使うと、大文字と区別しない
less() { command less -iNMR "$@"; }
# grep colored
grepc() { command grep --color=always "$@"; }

##### ディレクトリ移動 #####
# z というコマンドを入れると、ディレクトリ移動が楽になる。非常にオススメ
# https://github.com/rupa/z
# よりモダンなz.luaがおすすめ
# https://github.com/skywind3000/z.lua

改行スタイル(web)

書籍風スタイル

「改行スタイルだって?」彼は呟いた。「そんなもの、雰囲気で良いのだよ、雰囲気で」

 雰囲気と言いつつも、段落と段落の間に空行を入れている。ある程度のルールはあるだろう。

「ちなみに、会話文の間に空行を挟むかは、好みだ」
「この場合、空行は入っていませんね」

準書籍風スタイル

「地の文と会話文の間に改行を入れないのが書籍風であるとするならば」
彼は紅茶を飲んだ。
「これが準小説風スタイルだ」
「二行目の地の文に、インデント(行頭の空白)が入っていないのが特徴ですね」

 書籍なら、会話文と地の地の文に、改行を入れないだろう。このスタイルは、彼もほとんど見かけないらしい。

段落切りスタイル

「改行スタイルには、ポイントが二つある。それは、いつ改行するか」

 彼は紅茶をすすった。

「そして、いつ空行を入れるかだ」

 このスタイルでは、一文区切りで改行する。
 一段落分の文章を入力したら、空行を挿入する。

 シンプルだ。
 が、横に広い端末からは読みづらいかもしれない。

意味区切りスタイル

 この文章、一見、段落切りスタイルと似ている。
 しかし、空行を入れるタイミングが、段落ではなく、意味の区切りにおいて行われる。

「したがって」
 彼は紅茶をすすった。
「会話文の間に地の文が入っても、空行を入れなくてよい」

 ひと段落がもっと長くても良いかもしれない。このように、複数の文を続けてから。改行したって。いい。
 ポイントは、空行を入れるタイミングだ。上の行とこの行の間には、空行を挿入しなかった。小さな意味の区切りで改行をし、大きな意味の区切りで空行を入れるということだ。
 このスタイルが、なろうで最もメジャだろう。不規則で分かりにくいようにも思うが。

 明日のご飯は、カレーライスにするかなぁ。

二空行のスタイル

「私はこのスタイルのファンなんだ」
「へー」
「特徴は、会話文の間に気軽に短い地の文を挟みにくくなくなることだ」
 
 
 ほら、縦に長くなる。そう彼はいい、紅茶をすすった。地の文は長めが基本で、リアルタイムな描写はあまりしないらしい。しかし、そこまで言い切ると、改行スタイルというよりも、特定の作風を指すことになるのでは無いだろうか。
 
 
「確かに制限が強い書き方かもしれない。故に、このスタイルの文体は、模倣するのに適している」
「私の心の声に答えないでください。……この形式では地の文と会話文、どちらかが連続することが多いみたいですね」
「そうだ。だから今、私は紅茶を飲みたいのだが、飲むと長々とした地の文が始まってしまうから、ためらっている」
「私が飲んであげますね」
「おい」
 
 
 彼の紅茶をぐっと飲んだ。香りの良さと焦り具合に、思わず顔がほころんでしまう。彼は何か言いたがっていたが、今口を挟むと、二つ空行が入ってテンポが悪くなるため、黙っているようだ。黙らざるを得ないのが、このスタイルの暗黙のルールだった。

 私は上機嫌で新聞を開いた。まだ彼は干渉できない。会話文と会話文の間には、三段落以上の地の文が挟まるのが美しい、そう彼は感じるからだ。しかし、そろそろ限界みたいだった。
 
 
「おいっ、間接キスだぞ!!」
「あっ」

スリザリンの継承者―魔眼の担い手―

info

 作品: スリザリンの継承者―魔眼の担い手―
 作者: 寺町朱穂
 原作: ハリー・ポッターType Moon
 状態: 連載中(69話 52万字) → 本編完結(101話 97万字)

review

 主人公(セレネ)の、擦り切れた自我から物語は始まります。等身大の想いの下には、善も悪も無く、ただただ切実に、願いの実現を求めます。ただし手段は問いません

 表面上、セレネにはスリザリンっぽさがありません。器量良く、装いであるとしても優しく、非・純血主義で、他人とつるもうとしません。

 でも暴君もできる

 不穏な気配がありつつも、スリザリンな人々が可愛い! スネイプが優しい! ハーマイオニーがいいやつ! セレネって真面目!

 振り返ってみると、少女漫画的なフィルタが入っている気がします。マルフォイがデレるとかありえん。

 でもそれがいい。

 年を経るごとに、物語や人間関係が展開し、セレネにも変化が現れて来ました。荒波を乗り越えるべく、彼女は力を求めます。どう舵を切るか、もしくはどのような波に呑まれるのか。まだ読者には分かりません。

 誰よりも正しいハーマイオニーがセレネに肯定的で、誰よりも間違えるロン・ウィーズリーが否定的なのですが、はてはて。あの不穏な雰囲気が変わってきたように思うのは、僕の変化か、セレネの変化か。

 続きが読みたいものですね。

追記

 完結しました。おめでとうございます。あれだけ読みたかったのに、終わってしまうと寂しいものです。様々感傷が残りますが、セレネの行く道を祝福したいと思います。