6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

技術ブログ用の参考URLリンクを作成してクリップボードにコピーしてくれるコマンドを作ってみた

Last updated at Posted at 2018-04-28

はじめに

技術ブログを書くとき、参考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に読み替えてください)

.zshrc
# プレーンな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とコマンドを打つと、

  1. htmlのページを取得して
  2. titleタグを抽出して
  3. リンクに置換して
  4. 結果をクリップボードにコピーしつつ標準出力

してくれます。後はそのまま執筆中のブログ記事などにペーストしちゃってください。
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はalmlどちらも共通で、パイプ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 クリップボードにあるデータを標準出力に出力する

pbcopypbpaste はどちらも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 はフォーマットをちょっと変えるだけだったのですぐできました。

image.png

こうなると、もはやalはいらないかなとも思ったのですが、せっかく作ったのでどちらも載せることにしました。

終わりに

僕はこれでリンク作成が楽になりましたー。ちなみにこの記事のリンクは全てal で作ってみました…!
もしもっとこうした方がいいよなどあれば教えていただけるとありがたいです!

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?