Skip to content

Instantly share code, notes, and snippets.

@podhmo
Created February 11, 2026 05:58
Show Gist options
  • Select an option

  • Save podhmo/4e04e76bc803727d32629d051163a547 to your computer and use it in GitHub Desktop.

Select an option

Save podhmo/4e04e76bc803727d32629d051163a547 to your computer and use it in GitHub Desktop.
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)
var (
wslOutput = flag.Bool("wsl", false, "出力をWSL形式 (/mnt/c/...) に変換")
filter = flag.String("f", "", "Program Files検索時のディレクトリパスフィルタ(contains, case-insensitive)")
)
func nativeToOutput(path string) string {
if !*wslOutput {
// WSL実行時はWindows形式に戻す
if runtime.GOOS == "linux" {
if len(path) >= 6 && strings.HasPrefix(path, "/mnt/") && path[5] == '/' {
drive := strings.ToUpper(string(path[4]))
rest := strings.ReplaceAll(path[6:], "/", "\\")
return drive + ":\\" + rest
}
}
return path
}
// WSL形式に変換(Windows実行時のみ必要)
if runtime.GOOS == "windows" {
vol := filepath.VolumeName(path)
if vol == "" {
return path
}
drive := strings.ToLower(string(vol[0]))
rest := strings.ReplaceAll(path[len(vol):], "\\", "/")
rest = strings.TrimLeft(rest, "\\/")
return "/mnt/" + drive + "/" + rest
}
return path // 既にWSL形式
}
func searchInPATH(command string) ([]string, error) {
if command == "" {
return nil, nil
}
pathEnv := os.Getenv("PATH")
if pathEnv == "" {
return nil, fmt.Errorf("PATH環境変数が空です")
}
exeName := command
if !strings.HasSuffix(strings.ToLower(command), ".exe") {
exeName = command + ".exe"
}
sep := string(os.PathListSeparator)
dirs := strings.Split(pathEnv, sep)
var found []string
seen := make(map[string]bool)
for _, d := range dirs {
d = strings.TrimSpace(d)
if d == "" {
continue
}
for _, name := range []string{exeName, command} {
full := filepath.Join(d, name)
if info, err := os.Stat(full); err == nil && !info.IsDir() {
outPath := nativeToOutput(full)
if !seen[outPath] {
seen[outPath] = true
found = append(found, outPath)
}
}
}
}
return found, nil
}
func getProgramFilesRoots() []string {
var candidates []string
if runtime.GOOS == "windows" {
candidates = []string{
`C:\Program Files`,
`C:\Program Files (x86)`,
}
} else {
candidates = []string{
"/mnt/c/Program Files",
"/mnt/c/Program Files (x86)",
}
}
var roots []string
for _, c := range candidates {
if info, err := os.Stat(c); err == nil && info.IsDir() {
roots = append(roots, c)
}
}
return roots
}
func findInProgramFiles(command, dirFilter string) ([]string, error) {
roots := getProgramFilesRoots()
if len(roots) == 0 {
return nil, fmt.Errorf("Program Files ディレクトリが見つかりません")
}
var results []string
lowerFilter := strings.ToLower(dirFilter)
baseCommand := strings.TrimSuffix(command, ".exe")
baseCommandLower := strings.ToLower(baseCommand)
for _, root := range roots {
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
if os.IsPermission(err) {
return filepath.SkipDir
}
fmt.Fprintf(os.Stderr, "警告: %s をスキップ: %v\n", path, err)
return nil
}
// ディレクトリの場合:ルート自身は必ず探索、サブディレクトリのみフィルタ適用
if info.IsDir() {
if path != root && dirFilter != "" && !strings.Contains(strings.ToLower(path), lowerFilter) {
return filepath.SkipDir
}
return nil
}
// ファイルの場合:.exe で、コマンド名に一致(拡張子あり/なし両対応)
if strings.HasSuffix(strings.ToLower(info.Name()), ".exe") {
nameLower := strings.ToLower(info.Name())
if strings.EqualFold(info.Name(), command) || // 完全一致(firefox.exe)
strings.EqualFold(info.Name(), baseCommand+".exe") || // firefox + .exe
strings.HasPrefix(nameLower, baseCommandLower) { // firefoxで始まるもの(アップデーターなども含むが実用的)
results = append(results, nativeToOutput(path))
}
}
return nil
})
if err != nil {
fmt.Fprintf(os.Stderr, "エラー (%s): %v\n", root, err)
}
}
return results, nil
}
func main() {
flag.Parse()
if len(flag.Args()) == 0 {
fmt.Println("使い方: go run main.go [-f フィルタ] <コマンド名> [-wsl]")
fmt.Println("例:")
fmt.Println(" go run main.go firefox.exe")
fmt.Println(" go run main.go -f firefox firefox.exe -wsl")
fmt.Println(" go run main.go -f \"Mozilla Firefox\" firefox.exe")
return
}
command := flag.Args()[0]
var allResults []string
seen := make(map[string]bool)
// 1. PATH検索(常に優先)
pathResults, err := searchInPATH(command)
if err != nil {
fmt.Printf("PATH検索エラー: %v\n", err)
} else if len(pathResults) > 0 {
fmt.Println("=== PATH から見つかった ===")
for _, p := range pathResults {
if !seen[p] {
seen[p] = true
allResults = append(allResults, p)
fmt.Println(p)
}
}
fmt.Println()
}
// 2. Program Files検索(-f 指定時のみ)
if *filter != "" {
pfResults, err := findInProgramFiles(command, *filter)
if err != nil {
fmt.Printf("Program Files検索エラー: %v\n", err)
} else if len(pfResults) > 0 {
fmt.Printf("=== Program Files から見つかった(ディレクトリパスに \"%s\" を含む)===\n", *filter)
for _, p := range pfResults {
if !seen[p] {
seen[p] = true
allResults = append(allResults, p)
fmt.Println(p)
}
}
} else {
fmt.Printf("Program Files内に \"%s\" を含むディレクトリで %s が見つかりませんでした。\n", *filter, command)
}
} else if len(pathResults) == 0 {
fmt.Println("ヒント: PATHに見つからない場合、-f <文字列> を指定して Program Files を絞り込んで検索できます。")
}
if len(allResults) == 0 {
fmt.Printf("%s が見つかりませんでした。\n", command)
}
}
@podhmo
Copy link
Author

podhmo commented Feb 11, 2026

使いやすくはない。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment