2016年6月26日日曜日

grepとbashとアスタリスク*

grep a* といった
grepのパターンに*が含まれているとgrepに渡す前にbashがワイルドカードと解釈してカレントディレクトリにあるファイル名に展開してしまうので期待道理に動かないよという話。
echo *を実行してみると理屈がよくわかると思う。
grep "a*" のように常に引用符で括る癖をgrepに限らずつけたほうが良い。

2016年6月16日木曜日

シェルスクリプトで文字列を結合する


How to concatenate strings in shell script?

input file: input.txt
foo
bar 

expected results: output.txt
foo_awesome
bar_awesome

STR=_awesome
cat input.txt | xargs -n1 -d'\n' printf "%s %s\n" $STR | awk '{printf "%s%s\n", $2, $1}' > output.txt

Isn't it awesome?
解説
catでファイル内容を読み込みその内容とSTR変数をxargsを使いprintfの引数として渡す。
つまりprintf "%s %s\n" _awesome fooが一番目に呼ばれることになる。空白を挟んで _awesome foo という行を作り出し標準出力している。本当はここで整形を済ませたいがprintfコマンドが引数の順序を指定できず自由度が低いためawkのprintfで本格的に整形している。そして標準出力をファイルにリダイレクトして完了。


2 string variables version.

expected results: output.txt
prefoo_awesome
prebar_awesome
STR2=pre 
STR=_awesome
cat input.txt | xargs -n1 -d'\n' printf "%s %s %s\n" $STR $STR2 | awk '{printf "%s%s%s\n", $2, $3, $1}'  > output.txt

Shell script commandの根本的な解説

シェルスクリプトのコマンドがわかりにくい一因はその入出力方法が分かりにくいからであり、それさえ理解できればあとはなんとかなります。コマンド自体はただのC言語などで書かれたプログラムであり入出力方法の数はプログラムで可能な数だけありうるのです。

よく使われる入力方法としては
標準入力
プログラム引数
ファイル
があり
出力方法としては
標準出力
標準エラー出力 (コマンドの成否を表すメッセージなど)
ファイル
プログラムの戻り値(整数で正常終了した場合は普通0が返される)
があります。 入出力ともにネットワーク、プロセス間通信 etc などがありえます。

では次のコマンドを例に解説していきます。
cat foo.txt | sort
まずcatコマンドの引数として foo.txt を入力しています。そしてcatコマンドはその引数を元にファイル内容を読み込み標準出力へと出力します。そして | パイプで接続され標準出力の内容が次のプログラムsortへ標準入力として渡されます。sortは引数がなければ標準入力の内容をソートして標準出力へと出力します。そしてsortの標準出力の内容はこの後に何も指定していないため画面へと出力されます。

次のように
cat foo.txt | sort > out.txt
> リダイレクトを使用することで標準出力の内容をファイルへと出力することができます。(この場合 out.txtはsortの引数として渡しているわけではありません。|や>はシェルの機能です。)

プログラム自体がファイルを直接出力するというパターンもあります。
gcc main.c -o app
gccコンパイラに引数としてmain.c -o appを渡しています。gccはmain.cファイルを読み込みコンパイルした内容をappという名前のファイルへ出力します。

さて入力方法としては引数と標準入力が主に使われていることがわかりました。またファイルとしての入力は引数でファイル名を指定して読み込むというパターンが多いこともわかりました。出力方法としては基本的に標準出力が使われていることもわかりました。標準出力は| パイプで接続すれば次の標準入力になることはわかりましたが、後続のコマンドの引数として入力するにはどうしたら良いのでしょうか。それにはxargsを使いましょう。

input.txt:
file1
file2
file3

cat input.txt | xargs cat

xargsは標準入力から渡された文字列をxargsの引数として渡されたcatの引数として呼び出します。つまりxargsは cat file1 file2 file3を呼び出します。catは複数のファイル名が引数として渡された場合はその内容を結合してから標準出力へ出力します。つまり上記コマンドはinput.txtに書かれているファイル名を読み込みそのファイル内容をすべて結合して画面に表示します。xargs -n1 catと指定すればcatに引数を最大1個だけ付加して呼び出します。つまりcat file1、cat file2、cat file3と3回に分けてcatが呼びだされます。catコマンドの場合は分割したところで出力結果は同じですが。

入力元出力先利用可能なコマンド
ファイル標準出力cat
引数標準出力echo、printf
標準入力引数xargs
ネットワークファイルwget
ネットワーク標準出力curl
入力元出力先シェルの機能
標準出力標準入力|
標準出力画面そのまま
標準エラー出力画面そのまま
標準出力ファイル>
標準エラー出力標準出力2>&1

引数の指定方法によって入力元や出力先を変化させることのできるコマンドはよくあります。
入力元出力先コマンド解説
標準入力標準出力grep パターン引数として渡したパターンを標準入力から検索し標準出力へ出力する
引数のファイル名標準出力grep パターン ファイル名引数として渡したパターンをファイルから検索し標準出力へ出力する

また多くのUNIXプログラムはファイル名を指定する部分に - を指定することでファイルの代わりに標準入力(または標準出力)を使用できます。
echo "int main(){}" | g++ -x c++ -