はじめに
この投稿はシェル初心者によるものです。
なので説明が不十分なところがあるかもしれません(誤りがあれば指摘お願いします)。
きっかけ
GitBash
はGit
コマンドやcd
くらいしか使っていなかったのですが、ネットワークの調子が悪くなったので、試しにgoogle.co.jp
にping
をうってみました。
$ ping google.co.jp
google.co.jp [172.217.31.163]▒▒ ping ▒𑗐M▒▒▒Ă▒▒܂▒ 32 ▒o▒C▒g▒̃f▒[▒^:
▒v▒▒▒▒▒^▒C▒▒▒A▒E▒g▒▒▒܂▒▒▒▒B
▒v▒▒▒▒▒^▒C▒▒▒A▒E▒g▒▒▒܂▒▒▒▒B
▒v▒▒▒▒▒^▒C▒▒▒A▒E▒g▒▒▒܂▒▒▒▒B
▒v▒▒▒▒▒^▒C▒▒▒A▒E▒g▒▒▒܂▒▒▒▒B
172.217.31.163 ▒▒ ping ▒▒▒v:
▒p▒P▒b▒g▒▒: ▒▒▒M = 4▒A▒▒M = 0▒A▒▒▒▒ = 4 (100% ▒̑▒▒▒)▒A
ちなみに、文字コードはUTF-8に設定済みです。
文字コードの変更の仕方:
Windows
の場合、Git Bash
の画面の適当なところで右クリック→Options
でオプション画面を開き、左のメニューからText
を選択するとLocale
とCharacter set
のプルダウンメニューが表示されるので適切な値に変更します。たぶん、ja_JP
とUTF-8
でいいと思います。
なぜ文字化けするのか?
ping
やipconfig
などのWindows
のコマンドはShift_JIS
(CP932
)で出力されるようになっています。なので、Git Bash
で文字コードをUTF-8
にすると、Shift_JIS
で出力されたものをUTF-8
で表示しようとするため文字化けが起こります。
あっさり解決
Git Bash
の文字コードをShift_JIS
にすれば文字化けは起こらなくなりますが、それはやりたくないので。"git bash 文字化け ping"でググったところGit Bashでのpingの文字化けを解決という直球なタイトルの記事が引っかかり、その記事で紹介されている以下のシェルスクリプトを
.bashrc
ファイルにコピペし、Git Bash
を再起動したところping
の結果が文字化けしないようになりました。
function wincmd() {
CMD=$1
shift
$CMD $* 2>&1 | iconv -f cp932 -t utf-8
}
alias ping='wincmd ping'
$ ping google.co.jp
google.co.jp [172.217.31.131]に ping を送信しています 32 バイトのデータ:
要求がタイムアウトしました。
要求がタイムアウトしました。
要求がタイムアウトしました。
要求がタイムアウトしました。
172.217.31.131 の ping 統計:
パケット数: 送信 = 4、受信 = 0、損失 = 4 (100% の損失)、
(タイムアウトしてますが)めでたし、めでたし……
.bashrcファイルの場所:
デフォルトだとC:\Users\<ユーザ名>
にあります。
謎のシェルスクリプトを紐解く
文字化けはされなくなりましたけど、コピペした内容がさっぱりで気持ち悪いので自身の勉強がてら解説いたします。わかりやすくするため、先頭に行番号を付けました。
1 function wincmd() {
2 CMD=$1
3 shift
4 $CMD $* 2>&1 | iconv -f cp932 -t utf-8
5 }
6 alias ping='wincmd ping'
エイリアスの設定
function
は文字通り関数を意味していて、1~5行目はwincmd
という名前の関数の処理を表しています。6行目のalias
はping
というコマンド名をwincmd ping
という名前に変更しています。
つまり、ping google.co.jp
と入力してコマンドを実行するとwincmd ping google.co.jp
に置き換えられ、関数wincmd
が呼び出されることになります。
引数を取得する
普段Java
を書いているので()
内が空にもかかわらず引数が渡ってくるというのが理解しにくい点なのですが、関数内で$<num>
(numは1以上の数字)と書くと対応したインデックスの引数を取得することができます。引数はスペース区切りで複数渡すことができ、インデックスは先頭から割り振られるので、今回のケースでは、
$1 = ping
$2 = google.co.jp
となります。
$0
:
$0
だとスクリプト名を取得できるようです。
なので、2行目では変数CMD
にping
が代入されます。
引数を操作する
3行目で唐突にshift
という文字列が出てきますが、これは引数の配列のインデックスを1つ手前にずらします。今回のケースでは、$2
のgoogle.co.jp
が$1
に格納され、$1
のping
は削除されます。よって、引数の状態は、
$1 = google.co.jp
となります。
shift
の引数:
ずらすインデックスの数は任意に設定することができ、shift <num>
(numは1以上の数字)のように書きます。省略するとshift 1
と解釈され、今回のように1つずれます。
標準出力
ここが一番の難所でした。
4行目には|
がありますが、これは左の処理の出力を右の処理に渡すという演算子なので、$CMD $* 2>&1
とiconv -f cp932 -t utf-8
の2つの処理からなっていることがわかります。
$CMD
には、2行目の処理によりping
が格納されていて、$*
は今の引数全体を表しているので、google.co.jp
を指します。
shift
で1つずらしたのは、コマンド名と引数を分けるためだったんですね。
で、その後の2>&1
ですが……
>
はおなじみの比較演算子で、&
はコマンドをバックグラウンドで実行する演算子です。意味が分からず、途方に暮れているとteratailで2>&1はどういう意味?という質問を見つけました。回答によると、これはリダイレクトという機能で、標準エラー出力を標準出力に出力するという高頻出な書き方だそうです。
よって、$CMD $* 2>&1
はコマンド ping google.co.jp
の標準出力・標準エラー出力を右側の処理に渡すという意味になります。
文字コード変換
iconv
はInternational Codeset Conversion Library
の略で文字コード変換をするコマンドです。使い方はiconv -f <変換前の文字コード> -t <変換後の文字コード>
です。
なので4行目は、"ping google.co.jp
の標準出力・標準エラー出力"を"Shift_JISからUTF-8に変換する"という意味になります。
まとめ
Windows
のコマンドを実行する際に、エイリアスで独自定義した関数を呼び出し、その中でコマンドを実行し、出力結果をUTF-8
に変換して返却しました。個人的には難解でしたがインターセプタっぽくて面白かったです(小並感)。
今回はping
を例に取り上げましたけど、エイリアスを追加すれば他のWindows
コマンドにも対応できると思います。
投稿に至った経緯
なんとなく実行したping
が文字化けして困ったので調べた内容をメモがてらまとめました。しかし、Git Bash
でWindows
のコマンドを実行するケースはありそうなのですが、皆さんはどのように回避しているのでしょう?
- ↑のような設定を各
Windows
コマンドでやっている -
Windows
コマンドはコマンドプロンプトで実行している -
Git Bash
の文字コードをShift_JIS
にしている - そもそも
Windows
を使っていない
のどれかだと思うんですけど
補足
この記事を書き終わってから、知人にexplainshellというサイトを教えてもらいました。このサイトのテキストフィールドにシェルコマンドを入力すると、各コマンドや演算子ごとに詳しく説明してくれます。英語が読める方にはお勧めです。