シェルだけで色々こなす、便利なシェル芸


## シェルだけで、ファイル名だけ変更、またはファイルを一括指定

ファイルパス展開で{a,b,c}記法を使うと、パスの一部（例：ファイル名）だけが違う複数ファイルを簡単に指定できます。

```bash:
mv /the/long/long/path/to/file /the/long/long/path/to/new_file
```

これが

```bash:
mv /the/long/long/path/to/{file,new_file}
```

と簡単にできます。zshなら補完も可能です。
ファイル以外でも下記のようにテキストを展開することができます。

    $ echo hoge{fuga,piyo,muga}
    hogefuga hogepiyo hogemuga


## シェルだけで、連番のファイルをまとめて選択

ファイルパス展開で[012]記法を使うと、連番のファイルを選択することができます。

```bash:
cat /blah1 /blah2 /blah3 /blah4
```

これが

```bash:
cat /blah[1234]
```

と簡単にできます。
（存在しないファイルはスキップされます。）

## シェルだけで、落ちても自動で再起動

```bash:
while true; do target_script; sleep 1; done
```

終了する際はCtrl-Cを連打してください。

## シェルだけで、forループのワンライナー

（これは基本かもしれませんが、なんだかんだで重宝します・・）

```bash:
for i in {1..100}; do target_script $i; done
```

## （sedだけど）シェルだけで一括置換

sed -iを使うと、ファイルの中身を直接書き換えることができます。
例えば、dictionaryをdictonaryとtypoしてしまったケースがたくさんあるとすると、下記のコマンドで置換できます。（必ずgit等で前の状態に戻せるようにしておいてください！）

```bash:
sed -i '' -E 's/(d|D)ictonary/\1ictionary/g' **/*.[mh]
```

`-i ''`は、置換結果を元のファイルに書き出すオプションです。`-i '.new'`とすれば、入力ファイル.newのように、.newが末尾についたファイル名で保存されます（＝上書きされません）。
-Eは高度な正規表現を有効にするためのオプションです。

ファイル数が多い場合はagで絞ってから置換してもよいでしょう。

```bash:
sed -i '' 's/some_bad_method_name_or_something/good_one/g' `ag -l some_bad_method_name_or_something ./lib`
```

下を.bashrcや.zshrcなどに貼っとくと便利です

```bash:
sed-inplace () {
    if (( $# < 2 )); then
        echo "sed-inplace FROM TO [PATH ...]"
        return 1
    fi
    from="$1"
    to="$2"
    shift 2
    ag -l "$from" "$@" | while read file; do
      if [ "`uname`" = "Darwin" ]; then # please, please give me portable sed...
        sed -i '' -e "s/$from/$to/g" "$file"
      else
        sed -i'' -e "s/$from/$to/g" "$file"
      fi
    done
}
```

https://gist.github.com/ypresto/20213882618c6e650c35

※ag/ackがない場合はgrep -Rで代用できますが、.gitが置換対象に含まれないよう注意してください
※Mac (BSD?)では`-i ''`、Linuxでは`-i''`と書く必要があります。
※`()`が含まれているとsedが拾ってしまうので、`\(`のようにエスケープしてください

## シェルだけで、並列実行

http://stackoverflow.com/questions/5547787/running-shell-script-in-parallel

gnu parallelコマンドを使うか、bashに内蔵されたバックグラウンド実行機能（&やbgコマンドを使うもの）＋プロセスの終了待ちをするwaitコマンドを使えば簡単に並列実行できます。

### parallel篇

```bash:
cat url_list.txt | parallel wget
```

parallelコマンドは入力を改行で区切り、指定されたコマンドの後ろに付けて実行します。並列実行数はデフォルトでCPUコア数（スレッド数？）と同じですが、--jobsで指定もできます。

### bashだけ篇

下記のコードは[stackoverflow](http://stackoverflow.com/questions/5547787/running-shell-script-in-parallel)の引用です。 (Quoted from stackoverflow.)

```bash:
#!/bin/bash
for i in {1 1000}
do
   ( Generating random numbers here , sorting  and outputting to file$i.txt ) &
   if (( $i % 10 == 0 )); then wait; fi # Limit to 10 concurrent subshells.
done
wait
```

さすがに最大ジョブ数で管理することはできず、10個立ち上げたら全部終わるのをまってから、また10個立ち上げて・・という流れになります。

## （おまけ的な）シェルだけで、後置ならぬ前置if文や三項演算子っぽい何か

```bash:
some_command && echo success!
some_command || echo fail...
some_command && echo success! || echo fail...
```

```bash:
let cnt=0;

while ...; do
    some_processing;
    let cnt++;
    (( $cnt >= 10 )) && break;
done 
```

## （おまけ2）xargsでシェルスクリプトを渡す

bash -cを使えばシェルスクリプトの文字列を直接実行でき、これとxargsを組み合わせればechoの中でバッククォート展開を叩いたりできます。
`xargs -i`で{}がファイルパスに置き換わる（Mac OS X（おそらくBSDも）は`xargs -I'{}'`と明示的な指定が必要）ます。

下記は「HogeClass」という文字が含まれているファイルが最後にcommitされた日を表示する例です。

```bash:
ag -l 'HogeClass' | xargs -n1 -i bash -c 'echo "{}: `git log -n 1 --pretty=format:%cd -- {}`"'
```

※シェル芸というワードは[こちら](https://twitter.com/usptomo)のtwitterアカウント名から頂きました