Bash
Zsh
shell
125
どのような問題がありますか?

この記事は最終更新日から5年以上が経過しています。

投稿日

更新日

Organization

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

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

ファイルパス展開で{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
}

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

シェルだけで、並列実行

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アカウント名から頂きました

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
yuya_presto
[a.k.a. ypresto] Android StudioとXcodeを同時起動しながらAtomでRubyとかES6とか書きつつgitでmergeしてpushする日々。
codetakt
学習管理システム「schoolTakt」を運営するEdTechのスタートアップ企業

コメント

$(seq 1 1000)は{1..1000}で代用可能です。

1

ありがとうございます!

0

@tsuyoshi_cho さんから{1..100}{1 100}と誤字ってる箇所の修正をいただきました、ありがとうございます!

0

バックグランドジョブ数はjobsとかで拾えるかも?あいまいですみませんがそんなコード書いてます。

0
どのような問題がありますか?
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
データに関する記事を書こう!
~
新人プログラマ応援 - みんなで新人を育てよう!
~
125
どのような問題がありますか?
ユーザー登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
ユーザー登録ログイン
ストックするカテゴリー