† Mission †
作品:嘆きの亡霊は引退したい 〜最弱ハンターは英雄の夢を見る〜
作者:槻影 [Amazon]
に現れる、グレッグ様の数を数えよ!
> What's this?
グレッグ様を礼賛するネタ記事です。ネタ活動にもターミナルは役に立つ、という具体例としての意義はあります。
この程度なら、シェルが初見でも数時間で使えるようになる(なった)ので、やってみてください。こんなんできるし! という方はスルーしてください。
グレッグ様とは?
ベテラン冒険者です。調子に乗って『グレッグ様』を自称したため、今日の最新話でもネタにされています。一定の域にいる人物のため、未熟な冒険者がグレッグ様と呼ぶなら、あながち間違っていません。
† Solution †
1. 本作をダウンロードする
narou.rbを使いました。事前のインストールが必要です。
$ cd path/to/your/narou/directory
$ narou d https://ncode.syosetu.com/n6093en/
自力でhtmlをダウンロードしてパースするのもアリです。
2. テキストファイルを連結する (必要なら)
grepで再帰的な検索もできますが、lessで読むときに楽なため、連結しておきます。
ripgrepを使うと、より楽に再起検索ができます。
cd "$(grep)"
生データ(.txt)の入ったraw
ディレクトリを見つけ、移動します。GUIから移動しても構いません。CLIが初見なら、飛ばしてください。
最新のnarou.rbを使う場合は、
raw
ディレクトリにはhtml、本文
ディレクトリの方にtxtファイルが入っています。
実践
find
により再帰的にディレクトリを検索し、grep -v
でフィルタリングします。
$ pwd # print working directory /Users/moba/Desktop/narou $ # まずrawディレクトリを探す $ find . -type d | grep 嘆きの亡霊 # .は*でもいい ./小説データ/小説家になろう/n6093en 嘆きの亡霊は引退したい _最弱ハンターは英雄の夢を見る_ ./小説データ/小説家になろう/n6093en 嘆きの亡霊は引退したい _最弱ハンターは英雄の夢を見る_/本文 ./小説データ/小説家になろう/n6093en 嘆きの亡霊は引退したい _最弱ハンターは英雄の夢を見る_/raw $ # 3行目を抜き出す $ find . -type d | grep 嘆きの亡霊 | tail -1 # sed -n 3p や grep raw でも良い ./小説データ/小説家になろう/n6093en 嘆きの亡霊は引退したい _最弱ハンターは英雄の夢を見る_/raw $ # rawディレクトリに移動する $ cd "$(find . -type d | grep 嘆きの亡霊 | tail -1)"
備考
grep | cd "$(cat)"
とパイプを通すと、cdがサブシェルで実行されるために無効です。grep --color=always
だと、cd "$(grep)"
に失敗します。着色文字(ANSIエスケープコード)のためです。ただし、ターミナルの出力からは、エラーの原因が分かりにくいかもしれません。
プロンプトを短くする(必要なら)
パスをプロンプトに表示している場合は、短くした方が見やすいです。
$ pwd /Users/moba/Desktop/narou/小説データ/小説家になろう/n6093en 嘆きの亡霊は引退したい _最弱ハンターは英雄の夢を見る_/raw $ # パスがあまりに長かった $ PS1='$ ' # プロンプトを短くする
mobaの環境では、ターミナルの横幅に対して、プロンプトが長過ぎるとバグが出るため、短くしました。基礎編#3で作ったps-s
を使ってもいいです。
fishシェルの場合は、省略されたパス表示がデフォルトなので、プロンプト文字の変更は必要無いでしょう。
連結する
3つの改行で区切ってファイルの内容を連結します。
whileで連結する
標準出力は、消費しない限り書き足されていきます。そのため、ファイル毎にprintf
すれば、printf
した内容が連結された出力になります。
$ find * | sort -n # -n: naturally / numerically 1 1 メンバー募集.txt 2 2 メンバー募集②.txt <省略> $ find * | sort -n | while read f; do > printf '%s\n\n\n' "$(cat "$f")"; done > nageki.txt $ mv nageki.txt where/you/like/
別解: xargsで連結する
xargs -I
の実行に伴う文字{}
の置換は、コマンド置換$()
よりも後に行われます。$()
が処理順の優先順位を変えるものだと考えると、当然ですね。
代わりにbash -c
を使ってみました。
$ # GNU xargsで改行のみを区切り文字にする (-d: delimiter) $ find * | sort -n | gxargs -d '\n' -I{} bash -c 'printf "%s\n\n\n" "$(cat '{}')"' $ # もしくは、ヌル文字区切りで処理する (移植性と確実性から最善) $ find * -print0 | sort -zn | xargs -0 -I{} bash -c 'printf "%s\n\n\n" "$(cat '{}')"' $ # ファイル名が改行文字を含まないなら、trで\nを\0に変換してもいい (tr: translate) $ find * | sort -n | tr '\n' '\0' | xargs -0 -I{} bash -c 'printf "%s\n\n\n" "$(cat '{}')"'
シェルには基本的に文字列しか無いため、関数的に
find |> sort |> map cat |> join
と書けないのが残念です。PowerShellなら楽々書けるのでしょうか。
3. グレッグ様を数える
検索結果を数えます。ここで正規表現を使えると、会話文や地の文中のグレッグ様に限定できます。
grep: Global Regular Expression Print で検索する
grep
は、パタンにマッチするすべての行(段落のこと)を抜き出してくれるコマンドです。--color=always
で、マッチした部分を着色してくれます。
出力が過ぎる場合、| less -R
で閲覧するのもアリです。(-Rで着色を許す)
$ # 「グレッグ」を含む行を抜き出す(グレッグを着色して表示) $ grep --color=always グレッグ nageki.txt .. $ # -o --onll-matching; wc word count; -l: line $ # -o無しだと「グレッグ」を含む行の数を数えることになるため注意 $ grep --color=always -o グレッグ nageki.txt | wc -l 148 $ # 実はマッチ数を数えるオプションもある(grepの実装による?) $ grep -c グレッグ nageki.txt # -c: --count 148
4. グレッグ様の活躍を読む
マッチしたグレッグ様の前後の文章を読みたいとします。grep -<数値>
で、マッチ部分の前後段落も抜き出してくれますが、そもそもファイルは、lessで読むことができます。
less
the pager
less <file>
でファイルを読めます。&
キーでgrepに似た検索ができますが、件数は(たぶん)表示できません。
options
less -iNMR
のエイリアスを設定してもいいかもしれません。
オプション | 意味 | 効果 |
---|---|---|
i | ignore case | 検索時、小文字を大小文字を区別しない意味として扱う |
N | number | 行番号を表示 |
M | more verbose prompt | 左下のプロンプトを詳しくする |
R | raw | grepの出力を読むとき、着色を許す(結果的に) |
-M
では、ファイルの行数、表示されている行の範囲、スクロール位置(%)などが表示されます。
less内の操作
スクロール以外のショートカットをまず覚えます。
入力 | 意味 | 解説 |
---|---|---|
q | quit | 閉じる |
h | help | ヘルプ |
/ | search | 検索 |
n | next | 次の検索結果へ |
N | previous | 前の検索結果へ |
<数値>G | Go | 指定行へ飛ぶ |
& | grep風検索 | パタンを含む全行を表示 |
grep風検索で興味深いグレッグ様を見つけたら、& Enter
で検索を止めて、行番号G
で指定行へジャンプ! なお、検索/
は、ハイライトの代わりにも使えます。
lessには、GUIであるようなディレイが一切ありません。CLIのポテンシャルを感じます。
5. 余談: Vimでカウント
Vimでもlessと同様に/<pattern>
で検索できますが、やはりヒット数は表示されまん。
単語をカウントする
方法は色々ヒットしますが、『置換しない置換』gn
を使います。(n: do not substitute)
:%s/グレッグ//gn 148 matches on 147 lines :%s/グレッグ様//gn 67 matches on 67 lines :%s/「[^」]*グレッグ様[^」]*」//gn 6 matches on 6 lines
実質的に、単なるカウントですね。
note: 正規表現 (regex; regular expressions)
会話文のグレッグ様は、正規表現を使って検索・置換しました。今回使用した正規表現は:
文字 | 効果 |
---|---|
s/old/new/g | substitute old with new globally |
. | 改行以外の1文字 |
* | 0回以上のリピート |
[] |
[] 内で指定された一文字 |
^ | 否定 |
[^」]
で、」
以外の任意の一文字となります。最短マッチのために使いました。正規表現では、最長マッチがデフォルトです。
note: グレッグ様
全6件の「グレッグ様」発言がありました。4件がクライ、1件がティノによるもので、最初の1件のみが自称のようです。強く生きて……いや、とにかく生きて欲しい。
マッチさせられなかった「グレッグ様」
「グレッグ様」では検出できないグレッグ様がいました。
グレッグさん
ルーダの発言です。これは想定外でした。結局、自分の目で確認するのが安全かもしれません。そんな時も、grepやless内の&サーチが有効です。
グレッグ・ザンギフ様
こちらも漏らしていました。表記がブレるキャラの場合は、検索に工夫が要りそうです。
余談
grep マスターは神 nageki.txt
検出数は6、すべて同じ章の中です。同じネタを二度使わないのは流石ですね。
今後も心置きなく礼賛できるというものです。