Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

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

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

mv /the/long/long/path/to/file /the/long/long/path/to/new_file

これが

mv /the/long/long/path/to/{file,new_file}

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

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

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

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

cat /blah1 /blah2 /blah3 /blah4

これが

cat /blah[1234]

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

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

while true; do target_script; sleep 1; done

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

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

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

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

(sedだけど)シェルだけで一括置換

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

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

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

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

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

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

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篇

cat url_list.txt | parallel wget

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

bashだけ篇

下記のコードはstackoverflowの引用です。 (Quoted from stackoverflow.)

#!/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文や三項演算子っぽい何か

some_command && echo success!
some_command || echo fail...
some_command && echo success! || echo fail...
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された日を表示する例です。

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

※シェル芸というワードはこちらのtwitterアカウント名から頂きました

yuya_presto
[a.k.a. ypresto] Android StudioとXcodeを同時起動しながらAtomでRubyとかES6とか書きつつgitでmergeしてpushする日々。
http://ypresto.strikingly.com
codetakt
学習管理システム「schoolTakt」を運営するEdTechのスタートアップ企業
https://codetakt.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away