雑にシェルスクリプトのみを一覧取得するコマンド

ファイル名に拡張子 .sh が付いていれば見つけるのは簡単だが、拡張子なしのファイルも見つけたかったので、試行錯誤をメモ。

最初に結論

$ grep '^#!' -rn . | grep ':1:#!' | cut -d: -f1 | grep -v .git

試行錯誤

shebangで検索

$ grep '^#!' -rl .

行頭が #! ではじまるファイルを探している。

あんまりこれで困らないと思うが、欠点も存在する。一行目ではなく、ファイルの途中に #! があっても拾ってしまうのだ。

一行目のみに絞り込む

$ grep '^#!' -rn . | grep ':1:#!' | cut -d: -f1

最初のgrepで行番号も出力している。たとえば、こんな感じで出力される。

$ grep '^#!' -rn .
./install:1:#!/bin/sh
./README.md:33:#!/bin/sh
./hooks/build:1:#!/bin/bash

そして、 :1:#!grepかけて、一行目に含まれているファイル一覧に絞り込む。

$ grep '^#!' -rn . | grep ':1:#!'
./install:1:#!/bin/sh
./hooks/build:1:#!/bin/bash

最後にファイル名だけ取り出す。

$ grep '^#!' -rn . | grep ':1:#!' | cut -d: -f1
./install
./hooks/build

.gitを除外

$ grep '^#!' -rn . | grep ':1:#!' | cut -d: -f1 | grep -v .git

git 管理してるディレクトリ配下で実行すると .git ディレクトリ内も検索してしまう。そこで、単純に -v オプションで .git ディレクトリ配下を除外する。

おまけ:Makefile内で使う

最初、Makefileで定義するにあたって、普通にshell関数内で実行して、その結果を変数に格納していたが、なぜかshell関数は、実行結果の改行を空白に置換してしまって困った。

仕方ないのでマクロ定義して、それを呼び出すことにした。

define list_shellscript
    grep '^#!' -rn . | grep ':1:#!' | cut -d: -f1 | grep -v .git
endef

で、使うときはこんな感じ。

$(call list_shellscript)

xargsと組み合わせるときも、こんな感じで書ける。

$(call list_shellscript) | xargs -I {} echo {}

Makefileはフツーのシェルスクリプトとは異なる挙動をするので、なかなかにムズイ。