はじめに
どうも、Shakkuです。
都内某高専情報科の3年生です。(2023/12/8時点)
普段Chromeとかブラウザを使っていて、ブックマークに登録したいサイトが多すぎる!とか、ブックマークに入れたくはないけどよく開くサイトがあるな〜ってことありますよね?(あるよね...?)
ということで今回は、ターミナル上からWebサイトを「URL」で開いたり、「サイト名」で開いたり、「検索」したりできるCLIツールを作ってみました。
githubにリリースしているので、ぜひ使ってみてください。(https://github.com/Shakkuuu/website-cli)
仕様
CLI上で以下のコマンドを実行することで使用できる。
website-cli [-search 検索したい用語][site サイト名][url URL]
-search string
searchフラグでは、検索したい用語を入れることで、Googleでの検索結果が開かれる。
スペースを入れて複数用語を検索したいような場合は、クオーテーション'',""
で囲むか、用語を+
で繋ぐ必要がある。
-site string
siteフラグでは、あらかじめwebsite-cli-setting.jsonファイルで登録したサイト名を入力することで、そのサイト名に対応したサイトが開かれる。
-url string
urlフラグでは、開きたいWebサイトの(https://またはhttp://から始まる)URLを入力することでそのURLのサイトが開かれる(タイプミスに注意)
コマンドを実行するとデフォルトのブラウザで開かれる。
フラグを複数選択した場合は、site,url,searchの順で全て開かれる。
対応OS
対応をしているOSは以下の通り。
- darwin:amd64,arm64
- linux:386,amd64,arm,arm64
- windows:386,amd64,arm,arm64
(対応しているはず...)
使用方法
このURLhttps://github.com/Shakkuuu/website-cli/releasesから最新バージョンをダウンロードする。
ダウンロードしたzipファイルを展開し、適切な場所に配置する。(実行ファイルをMacとかであれば、/usr/local/bin/
とかに置く)
website-cli-setting.json
というファイルを、実行ファイルと同じディレクトリに配置する。
記述方法は以下の通り。
[
{
"url": "https://twitter.com",
"sitename": [
"twitter",
"Twitter",
"x",
"X",
"ツイッター",
"ついったー"
]
},
{
"url": "https://www.youtube.com",
"sitename": [
"youtube",
"Youtube",
"ようつべ",
"ユーチューブ"
]
}
]
配列のjsonで、url
とsitename
の項目が必要。
sitename
はstringの配列となっている。
url
には、そのサイトのURL、sitename
には、コマンドを使用する際に実行できるようにしたいサイト名を設定する。
website-cli
で実行!
コードの簡単な説明
構造体
jsonをGoの形式に読み込むための構造体。
jsonタグをつけて、jsonと構造体それぞれのフィールドを結びつける。
type SiteList struct {
Url string `json:"url"`
Sitename []string `json:"sitename"`
}
フラグ
ここで-site
,-url
,-search
などのフラグを取得。
flag.Stringなので、stringのみ対応。
一つ目の引数でフラグ名、二つ目の引数でデフォルトの値、三つ目の引数でフラグがなかった時に出力されたり、-help
時に表示される文章を与える。
// フラグ定義
urlFlag := flag.String("url", "", "開きたいWebサイトの(https://から始まる)URLを入力してください。")
siteFlag := flag.String("site", "", "(website-cli-setting.jsonで登録した)開きたいサイト名を入力してください。")
searchFlag := flag.String("search", "", "検索したい用語を入力してください。スペースを入れて検索したい場合は、クオーテーションで囲むか単語+でつないでください。")
flag.Parse()
設定ファイルの読み込み
os.Executable
で実行した実行ファイルのパスを取得。
os.Executable
で取得したパスは、実行ファイル名も入ってしまっているため、path/filepath
パッケージのfilepath.Dir
で実行ファイルの置かれているディレクトリのパスに変換する。
そこへfilepath.Join
で設定ファイル名を追加することで、設定ファイルを読み込むパスの完成。
os.ReadFile
で設定ファイルを読み込む。
定義していたSiteList構造体の配列型でjson.Unmarshall
を使用してjsonをGoの形式に読み込む。
// 実行ファイルのパスを取得
exePath, err := os.Executable()
if err != nil {
log.Fatal("os.Executable err:", err)
}
// 実行ファイルのディレクトリのパスに変換
exeDir := filepath.Dir(exePath)
// ディレクトリのパスに読み込む設定ファイルの名前を追加
settingfilePath := filepath.Join(exeDir, "website-cli-setting.json")
// 設定ファイル読み込み
f, err := os.ReadFile(settingfilePath)
if err != nil {
log.Fatal("os.ReadFile err:", err)
}
// json読み込み
var sl []SiteList
err = json.Unmarshal(f, &sl)
if err != nil {
log.Fatal("json.Unmarshal err:", err)
}
OS確認
OSによって、webサイトを開くコマンドが違うため、実行しているデバイスのOSをruntime.GOOS
で確認する。
OSによって、webサイトを開く際のコマンドを返している。(Linuxは本当にこれなのか不明...)
func OSCheck() (string, error) {
switch runtime.GOOS {
case "windows":
fmt.Println("Windows使用中")
return "start", nil
case "darwin":
fmt.Println("Mac OSX使用中")
return "open", nil
case "linux":
fmt.Println("Linux使用中")
return "xgd-open", nil
default:
return "", errors.New("対応外のOSです")
}
}
フラグ確認
フラグを複数立てていても同時に実行できるように、switchで分岐するのではなく、毎回ifでフラグが立っているか確認している。
各フラグの関数でサイトを開くコマンドを生成し、OpenWebSite関数に渡してWebサイトを開いている。
if *siteFlag != "" {
// サイト名選択
cmd, err = Site(siteFlag, UsingOSCmd, sl)
if err != nil {
fmt.Println(err)
flag.PrintDefaults()
os.Exit(1)
}
// websiteを開く
err = OpenWebSite(cmd)
if err != nil {
fmt.Println("Webサイトを開く際にエラーが発生しました:", err)
os.Exit(1)
}
}
if *urlFlag != "" {
// URL指定
cmd, err = Url(urlFlag, UsingOSCmd)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// websiteを開く
err = OpenWebSite(cmd)
if err != nil {
fmt.Println("Webサイトを開く際にエラーが発生しました:", err)
os.Exit(1)
}
}
if *searchFlag != "" {
// 検索ワード指定
cmd, err = Search(searchFlag, UsingOSCmd)
if err != nil {
fmt.Println(err)
flag.PrintDefaults()
os.Exit(1)
}
// websiteを開く
err = OpenWebSite(cmd)
if err != nil {
fmt.Println("Webサイトを開く際にエラーが発生しました:", err)
os.Exit(1)
}
}
Site
フラグとOSごとのコマンドとSiteListを引数にもらって、SiteListを展開しながら登録されているかを確認していく。
登録しているものにフラグが引っ掛かったら、exec.Command
で実行するコマンドを生成。
func Site(siteFlag *string, UsingOSCmd string, sitelist []SiteList) (*exec.Cmd, error) {
// サイト名選択
for _, site := range sitelist {
for _, sn := range site.Sitename {
if *siteFlag == sn {
cmd := exec.Command(UsingOSCmd, site.Url)
return cmd, nil
}
}
}
return nil, errors.New("登録されていないサイト名です")
}
Url
フラグとOSごとのコマンドを引数にもらって、URLが正しい書き方かどうかを確認してから、exec.Command
で実行するコマンドを生成。
URLのプロトコルhttps://
またはhttp://
から始まっているかを確認している。
func Url(urlFlag *string, UsingOSCmd string) (*exec.Cmd, error) {
s_urlflag := *urlFlag
// URLが正しく入力されているか
if len(s_urlflag) < 7 {
return nil, errors.New("URLのプロトコルに誤りがあります")
} else if s_urlflag[:8] != "https://" && s_urlflag[:7] != "http://" {
return nil, errors.New("URLのプロトコルに誤りがあります")
}
cmd := exec.Command(UsingOSCmd, *urlFlag)
return cmd, nil
}
Search
フラグとOSごとのコマンドを引数にもらって、URLを作成する。
google.comの後に、/search
で検索、?q=
で検索ワードを指定して、そこにフラグの検索ワードを入れて、exec.Command
で実行するコマンドを生成。
func Search(searchFlag *string, UsingOSCmd string) (*exec.Cmd, error) {
// URL作成
url := "https://google.com"
path := "/search"
params := "?q=" + *searchFlag
cmd := exec.Command(UsingOSCmd, url+path+params)
return cmd, nil
}
Webサイトを開く
生成された実行コマンドを引数で受け取り、cmd.Start
で実行される。。
これで、デフォルトのWebブラウザで開かれる。
func OpenWebSite(cmd *exec.Cmd) error {
// コマンドを実行してWebサイトを開く
if err := cmd.Start(); err != nil {
return err
}
return nil
}
終わりに
今回は、ターミナルからWebサイトを開くCLツールを作成してみました。
Go言語はCLIツールを作成するのにも使用されるという話を聞いたことをきっかけに作ってみたのですが、調べているうちにCobra
というツールを見つけました。
しかし、今回は初めてのCLIツール作成ということで、シンプルに標準パッケージのみで作成してみました。
flag
やos.exec
は過去に使用したことがあったのですが、path/filepath
やruntime
などは初めて使用したため、触れてみてよかったです。
また、jsonの読み込みは、apiでのjsonデータの受け渡しばっかりでやっていたので、ディレクトリに置かれたjsonファイルを読み込むのにちょっと戸惑いました。
何かいいアイデアが思いついたら、また作成してみようと思います。
今回作成したCLIツールはGithubにリリースしていますので、ぜひ使用してみてください。(https://github.com/Shakkuuu/website-cli)