はじめに
技術ブログを書くとき、参考URLのリンクを載せることは頻繁にあると思います。僕は今までURLとタイトルをそれぞれコピーアンドペーストして作っていたのですが、案外面倒だなーと思っていました。
特に別タブで開く用のtarget="_blank" rel="nofollow noopener"
などをつけたaタグのリンクは作るのが面倒で、コマンド一発でリンク作成からクリップボードにコピーまでしてくれたらだいぶ楽なのになーと思っていました。
そこでこの記事では、参考URLからリンクを作成し、かつクリップボードにコピーしてくれるコマンドを前々から試したかったシェル芸っぽく作ってみたので、そちらについて書きたいと思います。
環境
- Mac OS X 10.13.3
- zsh 5.5.1
今回はzshを使ってますが、おそらくbashなどでも動くと思いますー。
tl;dr
下記の関数を.zshrc
にペーストして保存し、source /path/to/.zshrc
します。
(bashを利用されている方は.zshrc
を.bashrc
とか.bash_profile
に読み替えてください)
# プレーンなaタグを作成する場合
function al() {
curl $1 -sL | xmllint --html --xpath 2>/dev/null "//head/title" - | sed -e "s;<title>;<a href=\"${1//&/\&}\" target=\"_blank\" rel=\"nofollow noopener\">;g" -e "s;</title>;</a>;g" | pbcopy ; pbpaste
}
# Markdownのリンクを作成する場合
function ml() {
curl $1 -sL | xmllint --html --xpath 2>/dev/null "//head/title" - | sed -e "s;<title>;\[;g" -e "s;</title>;\]\(${1//&/\&}\);g" | pbcopy ; pbpaste
}
その後、こんな感じでal URL
またはml URL
とコマンドを打つと、
- htmlのページを取得して
- titleタグを抽出して
- リンクに置換して
- 結果をクリップボードにコピーしつつ標準出力
してくれます。後はそのまま執筆中のブログ記事などにペーストしちゃってください。
aタグの方は別タブで開くリンクの設定target="_blank" rel="nofollow noopener"
を入れていますが、不要な場合は削っちゃってください。
実行例
$ al https://railsguides.jp/getting_started.html
<a href="https://railsguides.jp/getting_started.html" target="_blank" rel="nofollow noopener">Rails をはじめよう | Rails ガイド</a>
$ ml https://railsguides.jp/getting_started.html
[Rails をはじめよう | Rails ガイド](https://railsguides.jp/getting_started.html)
コマンド解説
それぞれのコマンドでどんなことをやっているかを簡単に解説しますー
パイプごとの処理概要おさらい
パイプの処理概要を再掲します。パイプ1,2,4はal
とml
どちらも共通で、パイプ3だけ中身が異なります。
- パイプ1: htmlのページを取得
- パイプ2: titleタグを抽出
- パイプ3: リンクに置換
- パイプ4: クリップボードにコピーしつつ標準出力
パイプ1: htmlのページを取得
curl $1 -sL
項目 | 詳細 |
---|---|
curl | 指定したURLにリクエストを送信し、HTMLレスポンスを受け取る。通常はGETリクエストを送信し、標準出力にレスポンスを出力 |
$1 | コマンドライン引数。指定したURLが入る |
-s | --silentの省略形。データ取得のメーターやエラーメッセージを非表示にする |
-L | --locationの省略形。指定したURLでリダイレクトされたらリダイレクト先に再リクエストする |
まずはおなじみcurl
でhtmlページを取ってきています。必要なのはhtmlページだけなので、それ以外の情報は表示させないように絞りつつ、リダイレクトされてもページ取得できるようにしています。
ちなみにcurl
のオプションに-o -
をつけても標準出力に結果を出力してくれますが、何もつけなくてもデフォルトで標準出力してくれるので省略しています。
パイプ2: titleタグを抽出
xmllint --html --xpath 2>/dev/null "//head/title" -
項目 | 詳細 |
---|---|
xmllint |
xml形式のファイルをパースするコマンド |
--html |
html形式ファイルをパースする |
--xpath |
xpath形式でタグを検索する |
2>/dev/null |
標準エラー出力を/dev/nullに出力して非表示にする |
"//head/title" |
xpath形式で<head> タグ内の<title> タグを取得する |
- |
検索結果を標準出力に出力する |
Webスクレイピングでよく見るxpathでhtmlをパースしたかったので、xmllint
を使っています。LinuxやMacには標準で入っているコマンドだと今回初めて知りました…!
curlで取得したhtml形式のデータから、xpathでhead
以下のtitle
タグを抽出しています。またパース時の余計な標準エラー出力は不要なので、2>/dev/null
を使って省くようにしています。
ちなみにgrepだとgrep -o "<title>.*</title>"
みたいな感じで抽出できるのですが、<body>
タグ内にiframeなどを使って別の<title>
が入り込んでることもあると思います。その為、xpathで<head>
内の<title>
タグに限定したいと思い、xmllint
を使っています。
パイプ3: リンクに置換
# alの場合
sed -e "s;<title>;<a href=\"${1//&/\&}\" target=\"_blank\" rel=\"nofollow noopener\">;g" -e "s;</title>;</a>;g"
# mlの場合
sed -e "s;<title>;\[;g" -e "s;</title>;\]\(${1//&/\&}\);g"
項目 | 詳細 |
---|---|
sed |
stream editorの略。ファイルまたはパイプから入力されたデータを編集できる |
-e |
編集処理用コマンドを指定。-e を複数指定すれば複数のコマンドをまとめて実行できる |
"s;<title>;<a href=\"${1//&/\&}\" target=\"_blank\" rel=\"nofollow noopener\">;g" |
※al の場合文字列 <title> を<a href="URL" target="_blank" rel="nofollow noopener"> に変換 |
"s;</title>;</a>;g" |
※al の場合文字列 </title> を</a> に置換 |
"s;<title>;\[;g" |
※ml の場合文字列 <title> を[ に置換 |
"s;</title>;\]\(${1//&/\&}\);g" |
※ml の場合文字列 </title> を](URL) に置換 |
ここでは<title>
タグをリンクの形に愚直に置換する処理をしています。
多分一番わかりづらいのが${1//&/\&}\
の部分だと思います。ここでは${変数//検索文字列/置換文字列}
の形式の変数内文字列置換(変数内の検索合致した全ての文字列置換)を使っています。
コマンドラインから渡した引数$1
内のすべての文字列アンパサンド&
を、エスケープした\&
に置換しています。
sed
では検索でマッチした文字列をアンパサンド&
で参照するようになっているため、&
が入っているURLだとその部分が置換されてしまう挙動になってしまうため、こちらの処理を入れています。(&
はYouTubeのURLとかに入ってますよね)
パイプ4: クリップボードにコピーしつつ標準出力
pbcopy ; pbpaste
項目 | 詳細 |
---|---|
pbcopy |
パイプから入力されたデータをクリップボードにコピーする |
; |
command1 ; command2 において、command1 が実行完了したらcommand2 も実行する |
pbpaste |
クリップボードにあるデータを標準出力に出力する |
pbcopy
、pbpaste
はどちらもMacに搭載されているコマンドです。Ubuntuだとxsel
で同じような事ができるようです。
ここではpbcopy
でクリップボードにパイプで渡されてきた内容をコピーし、pbpaste
でクリップボードにコピーした内容をペーストすることで標準出力に表示しています。
pbcopy
だけでも用途として足りてはいるのですが、どんなリンクが作成されたか標準出力でも確認できたほうが良いなと思い、表示させるようにしています。
出力結果をクリップボードにコピーできるのは一手間省けた感があり、かなり使い勝手が良いなと感じています。
想定Q&A
これであらゆる参考URLのリンクを作ってくれるの?
UTF-8のページであればほとんど問題なくリンクを作れると思うのですが、EUC-JPなどの文字コードを使っているサイトだとまれに文字化けすることがあります。
今回文字コードの変換対応までは入れていないので、もし文字化けしたらお手数ですが手動でリンクを作成するか、iconv
とかnkf
とかでうまく文字コード変換してもらえたらと思います。
何でワンライナーをfunctionに登録しているの?
シェル芸というくらいなので、ワンライナーで完結するんじゃないの?という声が聞こえてきそうな気がしています。
今回はリンク作成を簡単にしたかったということもあり、ワンライナーを毎回コピー&ペーストするのは用途に合わないなと思っていました。
なのでワンライナーをalias
に登録すればいいかなーと思ってましたが、alias
だとコマンドラインの引数をパイプ間でうまく引き渡してくれないとのことで。。
function
を使えばできるとのことだったので、こちらを.zshrc
に登録して使う形にしました。
何でプレーンなaタグ用とMarkdown用の2つ作ったの?
当初はaタグを作成する用のal
しか作ってなかったのですが、記事を書いている最中に、Qiitaでは[TEXT](URL)
の形でよしなに<a href="" target="_blank" rel="nofollow noopener">
のリンクに変換してくれるのを知ったからですw
ちなみにml
はフォーマットをちょっと変えるだけだったのですぐできました。
こうなると、もはやal
はいらないかなとも思ったのですが、せっかく作ったのでどちらも載せることにしました。
終わりに
僕はこれでリンク作成が楽になりましたー。ちなみにこの記事のリンクは全てal
で作ってみました…!
もしもっとこうした方がいいよなどあれば教えていただけるとありがたいです!