複数ファイルのシバン(shebang)一覧

カレントディレクトリに以下のようなファイルがあります。もう十年以上も前のファイルで作った記憶もないものとします。
得体のしれないこれらのファイルがどのようなファイルか確認するには、どうすれば良いでしょうか。

~$ ls -l
合計 24
-rwxr-xr-x 1 takkete takkete  70  1月  2  2003 aa
-rwxr-xr-x 1 takkete takkete  79  3月  4  2003 bb
-rwxr-xr-x 1 takkete takkete 103  4月  5  2003 cc
-rwxr-xr-x 1 takkete takkete  69  5月  6  2003 dd
-rwxr-xr-x 1 takkete takkete  84  6月  7  2003 ee
-rwxr-xr-x 1 takkete takkete  88  7月  8  2003 ff
~$ 

まずは、lsの内容をじっくり見ることです。
実行権限(x)がついています。ファイルサイズからしてテキストのように思えますが、fileコマンドを使うと、指定したファイルがどのようなものか簡潔に分かります。

~$ file aa
aa: Bourne-Again shell script, UTF-8 Unicode text executable
~$ 

bashスクリプトであることとUTF-8のテキスト、かつ実行可能ファイルであることがわかりました。
今回のようにファイルが複数あるときは、簡単で、ワイルドカードを指定するだけで良いです。

~$ file *
aa: Bourne-Again shell script, UTF-8 Unicode text executable
bb: Perl script, UTF-8 Unicode text executable
cc: Python script, UTF-8 Unicode text executable
dd: a /bin/ruby script, UTF-8 Unicode text executable
ee: a /usr/bin/groovy script, UTF-8 Unicode text executable
ff: awk script, UTF-8 Unicode text executable
~$ 

どれか一つ実行してみます。
本来は内容確認してからですが、今回は中身を知ってるファイルなので大丈夫です。実行するときはパス(./)を指定するのを忘れずに。

~$ ./ee
HELLO
~$

HELLOと表示されました。ee以外のファイルも同じようにHELLOを表示するスクリプトとなっています。
スクリプトとして実行させるには、ファイルの一行目にシバン(shebang)が必要になります。aaを行番号付きで見てみましょう。

~$ cat -n aa
     1	#!/bin/bash
     2	
     3	# bashでHELLOと表示するスクリプト
     4	
     5	echo HELLO
~$ 

さて、シバンがどのように書かれているか全ファイル一斉にsedを使って確認できないでしょうか。
fileコマンドでは、file *だけで良かったですが、sedでも同じような指定方法で一覧取得は可能でしょうか。sed -n 1p *でどのような表示になるか確認してみましょう。

~$ sed -n 1p *
#!/bin/bash
~$ 

ファイルaaしか表示されませんでした。*を指定してもファイルが連結されていまい、先頭ファイルに該当するaaのさらに一行しか表示されません。
このような場合はheadを梃子にします。headで各ファイルを指定行数で連結した後に、sedで部分抽出します。
sedで部分抽出するためのheadの出力フォーマットを確認するため、行番号付きで表示してみましょう。

~$ head -1 * | cat -n
     1	==> aa <==
     2	#!/bin/bash
     3	
     4	==> bb <==
     5	#!/usr/bin/perl
     6	
     7	==> cc <==
     8	#!/usr/bin/python
     9	
    10	==> dd <==
    11	#!/bin/ruby
    12	
    13	==> ee <==
    14	#!/usr/bin/groovy
    15	
    16	==> ff <==
    17	#!/usr/bin/awk -f
~$ 

3行毎に各ファイルの内容が表示されています。先頭は各ファイルのファイル名が表示されています。
headでの表示フォーマットが確認できたので、次はsedでシバン部分を抽出します。2行目を先頭に3つ飛びでフィルタすれば良いので、sedにて2~3pを指定します。

~$ head -1 * | sed -n 2~3p
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/python
#!/bin/ruby
#!/usr/bin/groovy
#!/usr/bin/awk -f
~$ 

シバンの一覧となりました。
一覧にすることで、チェックしやすくなります。
今回間違いを入れていますが、どこが違っているでしょうか。
シバン一覧を得ることができたので、これらのシバンのパスが存在するか確認してみましょう。
まずは、cutコマンドでパスのみ抽出します。

~$ head -1 * | sed -n 2~3p | tr '!' ' ' | cut -d' ' -f2
/bin/bash
/usr/bin/perl
/usr/bin/python
/bin/ruby
/usr/bin/groovy
/usr/bin/awk

!を空白に変えて、cutコマンドでは空白をデリミタ(delimiter 区切り文字、分離記号)をすることで、2列目を抽出することができました。
headとsedとcutでこねくり回したのですが、実はawkではもっと簡潔に書くことができます。

~$ awk 'BEGIN{FS="[! ]"}FNR==1{print $2}' *
/bin/bash
/usr/bin/perl
/usr/bin/python
/bin/ruby
/usr/bin/groovy
/usr/bin/awk
~$ 

では、このリストを使ってパスの存在チェックをどのように実施すれば良いでしょうか。
すぐ思いつくのは、test -e オプションによるファイルの存在チェックでしょうか。if分岐を書くのも面倒です。
簡単な方法の一つはlsコマンドによるチェックです。lsは指定したファイルが存在しないとエラーを出力してくれます。使ってみましょう。
先ほどcutまでを実行したばかりですので、積極的に!!を使っていきます。

~$ !! | xargs ls -1 >/dev/null
awk 'BEGIN{FS="[! ]"}FNR==1{print $2}' * | xargs ls -1 >/dev/null
ls: /bin/ruby にアクセスできません: そのようなファイルやディレクトリはありません
~$ 

はい。/bin/rubyにアクセスできないと表示されました。
これが間違っていました。which rubyで正解パスがわかります。

最後にファイル内にかかれているファイルのタイトル(3行目)を出力して終わりにします。prコマンドを使った方法です。

~$ head -3 * | pr -5Jl1 | cut -f1,4
==> aa <==	# bashでHELLOと表示するスクリプト
==> bb <==	# perlでHELLOと表示するスクリプト
==> cc <==	# pythonでHELLOと表示するスクリプト
==> dd <==	# rubyでHELLOと表示するスクリプト
==> ee <==	// groovyでHELLOと表示するスクリプト
==> ff <==	# awkでHELLOと表示するスクリプト
~$ 

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA