shellcheck で ls | grep が怒られた…ls の出力をパースしてはいけない?

[投稿日] 2016年2月17日
[最終更新] 2017年2月21日

本記事は、シェルスクリプトの構文をチェックしてくれる便利なソフトshellcheckにおいて、ls | grep を使うなと言われることについて書いた記事です。ls | grep は定番の書き方だと思っていたのですが。色々調べてみたところ、特殊なファイル名が存在する場合、lsの出力は必ずしも正確ではないから、という感じでしょうか。

スポンサーリンク

ls をパースしてはいけない?

shellcheckというソフトウェアを使って、シェルスクリプトの構文チェックをしています。シェルスクリプトは色々と罠の多い言語ですし、私のように独学で使っているものにとっては嬉しいソフトです。echo内でも変数をダブルクォートで囲んでないよと言われるなどちょっとお節介なところもあったりしますが、忘れがちなところですし、` `はレガシーだから$( )にしようとか、forループにfindの結果突っ込むのは危ないとか、色々と教えてくれて有難い。で、今回 ls | grep で以下のような警告を受けました。

SC2010: Don’t use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.

英数字じゃないファイルネームのためにも、ls | grep を使わずにglobや条件式付きのforループで対応してください、というような内容だと思うのですが、何がまずいのでしょうか。なんとなく検索してみると、「ParsingLs – Greg’s Wiki」やら「shell – Why *not* parse `ls`? – Unix & Linux Stack Exchange」などに、あれこれと ls の出力をパースしたりforループにつっこんではいけない理由が考察されています。なかなか読むのに骨が折れそうで、ちょっと読みきれていません。まぁ、基本的に、 ls の出力は必ずしも実際のファイル名を正確に反映したものとは限らない、ということになるのだと思います。大事なそうなところではあるので、暇を見てしっかりと理解したいところですが、まぁひとまずそんなところで。

ls | grep の置き換え

とはいえ、個人的な運用においては、ls | grepでもまず問題になることはなさそうに思います。たとえばlsの出力結果に特殊文字が入り込むようなケースって、どんなんでしょう?まぁだからといって放置してよい理由にはならないでしょうし、素直になおすことにしました。forループで * を使え、というのは確かですし、そちらのほうがわかりよいとも思います。以下の感じでしょうか。

for file in *
do
...
done

ディレクトリだけといった条件があるなら、if文で[ -d ${file} ]; then なんちゃらみたいなのを挟めばよいでしょう。

ただ、条件を指定してファイルの数を数える時、ls | grep -c 条件 なんてしたりしますが、これもfind | wcのほうが良いのでしょうか。find | wcも、細かいことを言い出すとそれはそれで色々ありそうに思えますが、少なくともshellcheck で警告は出ないし、良いのかな?どうなんでしょう。まぁそもそも、考えてみると ls | grep なんちゃらというのは、一見して何がしたいのかわかりづらいですし、そういう意味でもfindのほうが良いような気もします

というわけで、たとえばディレクトリ、隠しファイル以外のファイルを数えるコマンドとして、以下の様な感じにしてみました。

#ディレクトリと隠しファイルを除外してファイルのみ検索
#before
ls -p | grep -cv '\/'
#after
find . -type f -maxdepth 1 -not -name '.*' | wc -l | tr -d ' '

before は、ls で -pオプションをつけるとディレクトリ末尾に / がつくことを利用したものです。こちらのほうがコマンド自体はシンプルです。after は find コマンドで同じようなことを実現したもので、もしmaxdepthが1でない場合、隠しファイルの除外はもう少し煩雑になります(参考:「Macのfindでhidden fileを除いて検索する方法 – Qiita」)。tr -d ‘ ‘ は無くても大丈夫なケースはありますが、まぁいらない空白は取り除いておいたほうが良いと思います。うーん、コマンドが長くなってしまいました……。条件によっては、やっぱり ls | grep が使いたくなるでしょうし、なんとも微妙ですねぇ……。

スポンサーリンク

コメントを残す

メールアドレスが公開されることはありません。