モノノフ日記

普通の日記です

シェルスクリプトノウハウ

昔の記事ですが、IBM DeveloperWorksで紹介されていたUnixコマンドをご紹介。たまに忘れるので備忘録用にまとめておきます。

1コマンドでディレクトリツリー生成

-pオプションを利用

$ mkdir -p tmp/a/b/c

中括弧を使えば複雑なツリーも1コマンドで

$ mkdir -p project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}

もし、-pオプションが使えないのならmkdirhierコマンドを

$ mkdirhier project/{lib/ext,bin,src,doc/{html,info,pdf},demo/stat/a}

展開するディレクトリの指定

$ tar xvf newarc.tar.gz -C tmp/a/b/c

※参考サイトに記述されているコマンドはzshでは動作しなかった

$ tar xvf -C tmp/a/b/c newarc.tar.gz

&&コントロール演算子

1つ目のコマンドが正常終了したとき、2つ目のコマンドを実行(正確にはExitステータスが0のとき)

cd tmp/a/b/c && tar xvf ~/newarc.tar.gz

||コントロール演算子

1つ目のコマンドが異常終了したとき、2つ目のコマンドを実行(Exitステータスが0以外のとき)

cd tmp/a/b/c || mkdir -p tmp/a/b/c

&&と||演算子の組み合わせ例

cd tmp/a/b/c || mkdir -p tmp/a/b/c && tar xvf ~/newarc.tar.gz -C tmp/a/b/c

変数のQuote

bashでは下記の動作になる。zshでは全て"tmp/*"という文字列を表示

~ $ ls tmp/
a b
~ $ VAR="tmp/*"
~ $ echo $VAR
tmp/a tmp/b
~ $ echo "$VAR"
tmp/*
~ $ echo $VARa

~ $ echo "$VARa"

~ $ echo "${VAR}a"
tmp/*a
~ $ echo ${VAR}a
tmp/a

長い入力時にはエスケープシーケンスを利用

\で複数行に渡る入力が可能

$ cd tmp/d/e/f || \
cmdor> mkdir -p tmp/d/e/f && \
cmdor cmdand> tar xvf ~/archives.tgz -C tmp/d/e/f

サブシェル内のコマンドリストを実行

サンプルはtarの展開リストをadminにメールする、という処理だと思うが動かず。$()で囲むとtar -vの結果が上手く取れてないっぽい。

追記

コメントでご指摘頂きました。$()じゃなくて、()ですね。。すいません。
$()の使い方についてはウノウラボで紹介されているのでそちらを参照ください。
ウノウラボ by Zynga Japan: シェル(bash)スクリプトを書くときのTips

$ ( cd tmp/a/b/c/ || mkdir -p tmp/a/b/c && \
> VAR=$PWD; cd ~; tar xvf -C $VAR archive.tar ) \
> | mail admin -s "Archive contents"

カレントシェルとしてコマンドリストを実行

$()のようにサブシェルとしてコマンドラインを実行せずに普通に同列として処理する、ってことでいいのかな?これもサンプルは上手く動かず。

追記

ここも${}と勘違いしていました。スイマセン。正しくは{}です!

$ { cp ${VAR}a . && chown -R guest.guest a && \
> tar cvf newarchive.tar a; } | mail admin -s "New archive"

xargs

参考リンク先ではfindとxargsを組み合わせて使いましょう、と書かれてます。その割にサンプルコードはfind使ってないんですけど。

$ ls -1 | xargs file
December_Report.pdf: PDF document, version 1.3
README: ASCII text
a: directory
archive.tar: POSIX tar archive
mkdirhier.sh: Bourne shell script text executable

grepでcount

wc -l使うより、grep -cの方が速いらしい

$ time grep and tmp/a/longfile.txt | wc -l
2811

real    0m0.097s
user    0m0.006s
sys     0m0.032s
$ time grep -c and tmp/a/longfile.txt
2811

real    0m0.013s
user    0m0.006s
sys     0m0.005s

あと、-oオプション使うとパターンマッチングのgrepが可能

$ grep -o and tmp/a/longfile.txt | wc -l

フィールド指定でマッチング

ただ単にgrepしてしまうと、関係ない箇所がヒットして引っ張ってくることもある

$ ls -l /tmp/a/b/c | grep Dec
-rw-r--r--  7 joe joe  12043 Jan 27 20:36 December_Report.pdf
-rw-r--r--  1 root root  238 Dec 03 08:19 README
-rw-r--r--  3 joe joe   5096 Dec 14 14:26 archive.tar

awkでフィールド指定して比較してやればOK

$ ls -l | awk '$6 == "Dec"'
-rw-r--r--  3 joe joe   5096 Dec 14 14:26 archive.tar
-rw-r--r--  1 root root  238 Dec 03 08:19 README

grep比較(catありなし)

cat→grepより、grepのみの方が若干速い

$ time cat tmp/a/longfile.txt | grep and
2811

real    0m0.015s
user    0m0.003s
sys     0m0.013s
$ time grep and tmp/a/longfile.txt
2811

real    0m0.010s
user    0m0.006s
sys     0m0.004s