Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
67
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@syui

Shell Script Tips 完全版

面白いTips

ファイルを読み込む

.

# sourceコマンドは短縮可能
. ~/.bashrc
# 同じ意味
source ~/.bashrc

ファイルの拡張子を変換する

in *.json, ${i%.*}.foo, ( *.json )

# ファイルが複数の場合
$ for i in *.json;do echo ${i%.*}.yml;done
> foo.yml, bar.yml

# 一つのファイルを変換(配列を利用するので、複数の場合も扱いやすくなる)
$ file=( *.json )
$ echo ${file[1]%.json}.yml

配列に値を追加する

( ${foo[@]} bar )

$ foo=( a b c )
$ foo=( ${foo[@]} d )

# 配列を削除
$ unset "foo[2]"

git logをHEADを参照し順に見ていく

%+9s %s, $(($o+1))

$ o=0; git log --oneline | while read l; do printf "%+9s %s\n" "HEAD~${o}" "$l"; o=$(($o+1)); done | less

# 表記法には他にも色いろある
printf "%.5e\n"

連番の作成

.., %04d

echo ${01..30}

# for
print "%04d" %i

出力を表示しない

/dev/null 2>&1

$ echo foo > /dev/null 2>&1
# バックグラウンド
$ echo foo > /dev/null 2>&1 &

右上に時刻を表示する

$(($(tput cols)-n))

$ while sleep 1;do tput sc;tput cup 0 $(($(tput cols)-29));date;tput rc;done &

出力の強制保存

>!

$ touch foo.json
$ echo "Shell Script!" >! foo.json

コマンド履歴を使った置換

:gs

$ echo -e https://qiita.com/api/v2/users/syui
# zshの書き方、bashでも同じことが出来るがzshよりも面倒
$ echo !-1:h:h:h:h:t:gs/qiita/github/
> github.com

フォーク爆弾

このコマンドは絶対に実行してはいけない。

:

$ :(){ :|:& };:

日付で前後を計算に入れる

-2 day

# Linux
$ date +"%Y-%m-%d" --date "-2 day"

少数演算を条件にする

#!/usr/bin/env bash
if [ $(echo "$a <= 0.5" | bc) -eq 1 ]; then;echo $a;fi

変数が未定義の場合にエラーを出す

${foo:?""}

$ foo=""
$ echo ${foo:?""}

バックグラウンド処理を待つ

wait $!

#/bin/bash
sleep 100 &
wait $!

位置パラメータを使用

$ foo=(123 abc ABC)
$ echo ${foo[2]:1}
> bc

計算する

letを使えます。exprなどもありますが。

$ let a=1+2
$ echo $a
> 3

変数の表記が使えない場合の配列の全行を参照する

eval

$ eval echo '${'$EVAL_VAL'[@]}'

基本的によく使うTips

変数への代入

Shell Scriptでは、変数へ代入するための幾つもの方法があります。例えば、Pythonでは少なく、Perlでは多いですが、Shell Scriptのそれは、これらの言語よりも遥かに複雑です。その理由の一つがターミナルに直結して処理を行うことにあると思われます。

そして、シェルにも様々な種類があり、微妙に変数の代入のやり方についても違いがあります。

# sh
$ foo=123
$ export foo

# bash
$ foo=123
or
$ export $foo

変数にも環境変数やシェル変数があります。環境変数は引き継がれるが、シェル変数は引き継がれないと覚えておけばよいでしょう。もちろん、環境変数であっても引き継がれない場合もありますが、使っているうちにわかってくると思います。ここで細かいことを言っても仕方ありませんし、細かいことを詳細に書いていく記事でもありませんので、次に進みます。

以下、主に、bashでのTipsになりますが、私は基本的にzshを使いますので、間違いがあるかもしれません。その場合はご指摘ください。

変数の峻別

通常は、普通に先頭に$を置くことで、変数を意味しますが、前後に置かれる文字列によっては違う意味にもなってきます。したがって、その場合は、{}で囲うことで、より正確な表記が可能です。これはどうなんだろうとわからない時は、出来る限り{}を使いましょう。可読性にも役立ちます。

$ echo $USER$SHELL
or
$ echo ${USER}${SHELL}

シングルとダブルクォート

"'では、変数を使えるか、厳格な文字列で解釈されるかで違ってきます。

$ echo "$SHELL"
> /bin/bash

$ echo '$SHELL'
> $SHELL

文字数を調べる

先頭に#を持ってきます。この特殊文字は先頭に置くことで、文字数を調べるときによく使われます。先頭#は文字数と覚えておけばよいです。

$ echo ${#foo}

配列を扱う

配列で扱うには、()で囲った上で、そこに正規表現や数字を使って値を取り出せます。シェルの配列は()と覚えておけばよいです。

$ foo=(a b c)
$ echo ${foo[1]}
$ echo ${#foo}

変数の抽出

変数のうちで特殊な表現を使い、条件に一致するものを取り出せます。URLやファイルの拡張子、ファイル名などを変数から取り出したい時に使います。

URL

$ url=http://qiita.com/syui

# ユーザーを取り出したい場合
$ echo ${url##*/}
> syui

# ホームアドレスを取り出したい場合 
$ echo ${url%/*}
> http://qiita.com

# プロトコルを取り出したい場合
$ echo ${url%%/*}
> http:

FILE

$ file=/home/syui/foo.json

# ディレクトリを取り出したい場合
$ echo ${file%/*}
> /home/syui

# ファイル名を取り出したい場合
$ echo ${file##*/}
> test.json

# 拡張子を取り出したい場合
$ echo ${file##*.}
> json

ZSH

ちなみに、zshにはこんなややこしい正規表現を使わなくても、以下のようにして変数からの抽出を行えます。取り出せる順で言うと、先頭から:h:tの順となります。headtailコマンドを想起して覚えると良いでしょう。ただし、これらは主に、コマンドラインで有効な表現です。気をつけて使いましょう。!!みたいなもので、履歴を利用します。

$ url=https://github.com/syui/foo.json

$ echo $url:r
> https://github.com/syui/foo

$ echo $url:h
> https://github.com/syui

$ echo $url:h:h:t
> github.com

$ echo $url:h:t
> syui

$ echo $url:t
> foo.json

$ echo $url:e
> json

履歴の利用

シェルには、コマンド履歴を扱うために役立つ特殊な変数が用意されています。

$ ls -a /home/syui/foo.json

# 例えば、直前のコマンドを実行したい場合
$ !!

# 直前のコマンドから値を取り出したい場合(!でも良いけど、できれば!!の方が良い)
$ echo !!0
> ls
$ echo !!1
> ls -a
$ echo !!2
> ls -a /home/syui/foo.json
$ echo !!$
> /home/syui/foo.json

ちょっと分かりにくいですが、こんな感じで、数字や正規表現を使えます。

ifのオプション一覧と数値の比較

よく使うものは、変数の有無である-zとファイルやディレクトリをチェックする-f, -dあたりです。-f, -dの代わりに、ファイルが存在する場合の-eを使ってもいいかもしれません。

プション 使用例 オプションの意味
-d test -d file file がディレクトリならば真となる。
-f test -f file file が普通のファイルならば真となる。
-s test -s file file が 0 より大きいサイズならば真となる。
-e test -e file file が存在するならば真となる。
-r test -r file file が読み取り可能ならば真となる。
-w test -w file file が書き込み可能ならば真となる。
-x test -x file file が実行可能ならば真となる。

testオプション

プション 使用例 オプションの意味
-z test -z string string の文字列長が 0 ならば真となる。
-n test -n string string の文字列長が 0 より大ならば真となる。

その他のifオプション

Primary Meaning
[ -a FILE ] True if FILE exists.
[ -b FILE ] True if FILE exists and is a block-special file.
[ -c FILE ] True if FILE exists and is a character-special file.
[ -d FILE ] True if FILE exists and is a directory.
[ -e FILE ] True if FILE exists.
[ -f FILE ] True if FILE exists and is a regular file.
[ -g FILE ] True if FILE exists and its SGID bit is set.
[ -h FILE ] True if FILE exists and is a symbolic link.
[ -k FILE ] True if FILE exists and its sticky bit is set.
[ -p FILE ] True if FILE exists and is a named pipe (FIFO).
[ -r FILE ] True if FILE exists and is readable.
[ -s FILE ] True if FILE exists and has a size greater than zero.
[ -t FD ] True if file descriptor FD is open and refers to a terminal.
[ -u FILE ] True if FILE exists and its SUID (set user ID) bit is set.
[ -w FILE ] True if FILE exists and is writable.
[ -x FILE ] True if FILE exists and is executable.
[ -O FILE ] True if FILE exists and is owned by the effective user ID.
[ -G FILE ] True if FILE exists and is owned by the effective group ID.
[ -L FILE ] True if FILE exists and is a symbolic link.
[ -N FILE ] True if FILE exists and has been modified since it was last read.
[ -S FILE ] True if FILE exists and is a socket.
[ FILE1 -nt FILE2 ] True if FILE1 has been changed more recently than FILE2, or if FILE1 exists and FILE2 does not.
[ FILE1 -ot FILE2 ] True if FILE1 is older than FILE2, or is FILE2 exists and FILE1 does not.
[ FILE1 -ef FILE2 ] True if FILE1 and FILE2 refer to the same device and inode numbers.
[ -o OPTIONNAME ] True if shell option "OPTIONNAME" is enabled.
[ -z STRING ] True of the length if "STRING" is zero.
[ -n STRING ] or [ STRING ] True if the length of "STRING" is non-zero.
[ STRING1 == STRING2 ] True if the strings are equal. "=" may be used instead of "==" for strict POSIX compliance.
[ STRING1 != STRING2 ] True if the strings are not equal.
[ STRING1 < STRING2 ] True if "STRING1" sorts before "STRING2" lexicographically in the current locale.
[ STRING1 > STRING2 ] True if "STRING1" sorts after "STRING2" lexicographically in the current locale.
[ ARG1 OP ARG2 ] "OP" is one of -eq, -ne, -lt, -le, -gt or -ge. These arithmetic binary operators return true if "ARG1" is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to "ARG2", respectively. "ARG1" and "ARG2" are integers.

シェルでは、数値比較は、正直覚えにくいです。演算。なので、((n<n))のほうが覚えやすいです。

プション 使用例 オプションの意味 数式
-eq test num1 -eq num2 num1 と num2 が等しければ真となる。 num1=num2
-ne test num1 -ne num2 num1 と num2 が等しくなければ真となる。 num1≠num2
-lt test num1 -lt num2 num1 が num2 より小ならば真となる。 num1<num2
-le test num1 -le num2 num1 が num2 以下ならば真となる。 num1≦num2
-gt test num1 -gt num2 num1 が num2 より大ならば真となる。 num1>num2
-ge test num1 -ge num2 num1 が num2 以上ならば真となる。 num1≧num2

追記 : 数値比較には、((1<3))などとして行うことができます。そちらのほうが覚えやすいです。

if文字列比較

# 同じ場合
if [ "foo" = "$bar" ];then
# 異なる場合
if [ "foo" != "$bar" ];then

orである-oandを意味する-aも使えます。

# || = -o, && = -a
if [ "foo" = "$bar" ] && [ "foo" != "$bar" ];then

case文の使用

私はcase文を好みます。文字列や数値の比較が自然にできます。

case $foo in
 [fF]oo|FOO) echo ok
 ;;
 [bB]ar|BAR) echo no
 ;;
 [fF]*) echo no
 ;;
esac

[a-z]*) #先頭が小文字なら
[!0-9]) #数字ではない文字列
???) #3文字の文字列

あまり使わない応用的なTips

変数の変換

変数はもう一度入れ直すことや、変換することで、中に入っているものを変えられます。シェルには現在の値を上手く変換する表現がいくつも用意されています。それはとても一般的で、汎用性があるものです。

# fooに数値が入っていなければ、文字列であるbarに変換する
$ ${foo:-bar}
> bar

$ foo=1
$ ${foo:-bar}
> 1

その他、以下の様な代入や書き方があります。

$ ${foo:+bar}

# 拡張子の変換
$ ls
$ file=( *json )
$ echo ${file%.*}.yml

任意の文字を指定して値を取り出す

これについては、正規表現でよく使われる?を使うとできます。

$ foo=123abc
$ echo ${foo#??}
> 3abc

その他

# 先頭が数字以外で始まるファイル
$ ls [^0-9]*

# 直前のlsから始まるコマンド
$ !ls:p

# for文の書き方
for (( i=1;i<=3;i++ ))
do
 echo $i
done

# コマンド結果を変数に入れる
foo=`ls`
foo=$(ls)

# testコマンドを利用する, オプションなどはif文と共通します
$ test "foo" = "foo"
$ test "foo" != "foo"

# 大文字を小文字に変換
$ tr '[A-Z] '[a-z]'

# 行数を指定する
$ awk "NR==1"

# ターミナル出力を操る
$ exec 1< foo
$ exec 1<&-
67
Help us understand the problem. What is going on with this article?
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
67
Help us understand the problem. What is going on with this article?