シェルは変数を文字列として取扱い、数値としては考えません。
数字であってもそれは数値ではなく文字として扱われているだけです。
計算処理を行うにはexpr
コマンドを利用します。
expr 1 + 1
2
expr 1 - 2
-1
expr 2 \* 2
4
expr 4 '*' 3
12
expr 4 / 2
2
expr 4 % 3
1
掛け算の*
は必ずクォーテーションをつける必要があります。
変数を使って計算させた数値を元の変数に代入する例です。
VALUE=1
VALUE=`expr $VALUE + 1`
echo $VALUE
2
数値の大小を比較する場合はtest
コマンドが利用できます。
test int1 -eq int2
test int1 -ne int2
test int1 -lt int2
test int1 -le int2
test int1 -gt int2
test int1 -ge int2
浮動小数点を含む計算を行う場合はbc
コマンドが利用できます。
小数第何位まで出力するかをscale=n
で指定します。
echo "scale=n; num1 + num2" | bc # num1にnum2を加える
echo "scale=n; num1 - num2" | bc # num1にnum2を引く
echo "scale=n; num1 * num2" | bc # num1にnum2を掛ける
echo "scale=n; num1 / num2" | bc # num1にnum2を割る
echo "scale=3; 10 / 3" | bc
3.333
echo "scale=4; 10 / 3" | bc
3.3333
echo "scale=3; 3.33 / 3.1234" | bc
1.066
echo "scale=7; 3.33 / 3.1234" | bc
1.0661458
echo "3.5678 / 3" | bc
1
echo "scale=2; 3.5678 / 3" | bc
1.18
cat is_numeric
#!/bin/sh
NUMBER=$1
expr "$NUMBER" + 1 > /dev/null 2>&1
if [ $? -lt 2 ]; then
echo "Numeric"
else
echo "Not Numeric"
fi
計算結果(標準エラー出力の結果を標準出力にマージしたもの)
を/dev/null
に捨てます。
$?
はコマンド実行時の終了ステータスを表す関数で、
$NUMBERの値が数値であれば、$?
には終了ステータス0
(計算結果が0の場合のみ1
)が代入されます。
また、$NUMBERの値が数値でなければ、、$?
には2
が代入されます。
上記スクリプトの実行例です。
./is_numeric 3
Numeric
./is_numeric
Not Numeric
COLUMN=5
ls -l | awk '{total += $'$COLUMN'} END {print total}'
awk
は1行ずつ行を読み込み処理します。
5番目のカラムの値を足していきます。
ENDの後は、全部の行を処理した後に実行されます。
ls -l
total 24
-rw-r--r-- 1 jun staff 17 3 2 22:28 aaa
-rw-r--r-- 1 jun staff 5 3 2 22:28 bbb
-rw-r--r-- 1 jun staff 24 3 2 22:28 ccc
COLUMN=5
ls -l | awk '{total += $'$COLUMN'} END {print total}'
STRING="abc def ghi"
STRING=`echo "$STRING" | sed -e "s/def/xyz/g"`
echo "$STRING"
abc xyz ghi
echo "$STRING" | sed -e "s/def/xyz/g"
で出力された文字列abc xyz ghi
が
標準出力に出力されますが、その値をSTRINGに再代入しています。
実行した結果をバッククォートで囲むことで、元の変数に代入できます。
文字列を連結するだけなら変数を並べて書くだけです。
STRING1=abc
STRING2=xyz
VAR=$STRING1$STRING2
echo $VAR
abcxyz
変数+文字列とする場合は中括弧でその変数部分を囲む必要があります。
STRING=abc
VAR=${STRING}xyz
echo $VAR
abcxyz
文字列 + 変数ならば中括弧は不要です。
STRING=xyz
VAR=abc$STRING
echo $VAR
abcxyz
変数にホワイトスペースが混在していても、echoコマンドで出力する際に シェルが余計なスペースを取り除いてしまうので、余分なホワイトスペースを削除できます。
STRING=`echo $STRING`
exprコマンドをexpr "string" : '.*'
のように使うと文字列の長さを得られます。
expr
は引数にコロン(:)があると、
右辺と左辺の文字列を比較して先頭から何文字までが等しいかという値を返します。
STRING=abcdefghij
NUMCHARS=`expr "$STRING" : '.*'`
echo $NUMCHARS
10
ただし上記のスクリプトでは、
STRING
にexpr
コマンドで利用する演算子が1文字だけ含まれていた場合、
expr
が演算子だと判断し、エラーとなってしまいます。
STRING="*"
NUMCHARS=`expr "$STRING" : '.*'`
echo $NUMCHARS
expr: syntax error
case
文で任意の1文字の時のみ分岐をさせると演算子が含まれていても
期待された結果が得られます。
STRING='*'
NUMCHAR=`case "$STRING" in
? ) echo 1 ;;
* ) expr "$STRING" : '.*' ;;
esac`
echo $NUMCHAR
1
grep
コマンドは実行終了ステータスとして、
文字列が含まれていたら真、含まれていなければ偽を返します。
それを利用して、文字列中にある文字列が含まれているかの判定が可能です。
echo abcdefghijklmnopqrstuvwxyz | grep xyz > /dev/null
if [ $? -eq 0 ]; then
echo "xyzが含まれます"
else
echo "xyzは含まれていません"
fi
grep
の実行結果は/dev/null
に捨てます。
ps -ax | grep jserver
#!/bin/sh
STRING=abcdefghijklmnopqrstuvwxyz
SUBSTRING=bcd
if echo "$STRING" | grep "$SUBSTRING" > /dev/null
then
echo "Found it."
fi
case文を使ってチェックすることも可能です。
#!/bin/sh
STRING=abcdefghijklmnopqrstuvwxyz
SUBSTRING=bcd
case "$STRING" in
*"$SUBSTRING"* ) echo "Found it." ;;
* ) echo "Not Found" ;;
esac
expr "abcdefghijklmn" : "a.*\(e..h\)i.*"
efgh
パターン | 説明 |
---|---|
.* | 何もないものを含め、どんな文字列をも表す |
. | 何か1個の文字(ヌル文字にも該当する) |
* | 直前の文字が0個以上並んだ文字列 |
. | ドット文字そのもの |
* | アスタリスクそのもの |
具体的な例 | 説明 |
---|---|
abc.* | abcで始まる文字列ならなんでも |
.*abc | abcで終わる文字列ならなんでも |
ab*c | acやabbbcというようにaとcの間にbが0個以上ある文字列 |
ab.*c | abで始まってcで終わる文字列 |
a....b | aで始まってbでおわる6文字の文字列 |
..*.c | .cで終わる文字列。.cの前に少なくとも1文字ある。 |
expr "string" : "pattern\(.*\)" # patternという文字列より後ろ
expr "string : "\(.*\)pattern" # patternという文字列より前
expr "string : "\(...\).*" # 初めの3文字
expr "string : ".*\(...\)" # 後ろの3文字
expr "string : "...\(.*\)" # 初めの3文字を取り去った部分
expr "string : "\(.*\)..." # 後ろの3文字を取り去った部分
expr "string : "\(.*\)" # その文字列をそのまま
#!/bin/sh
STRING=junpeko
CHAR=`expr "$STRING" : "\(.\).*"`
echo $CHAR
REMINDER=`expr $STRING : ".\(.*\)"`
echo $REMINDER
CHAR=`echo "$CHAR" | tr [a-z] [A-Z]`
STRING=$CHAR$REMINDER
echo $STRING
1行で書くと以下となります。
#!/bin/sh
STRING=junpeko
STRING=`expr "$STRING" : "\(.\).*" | tr [a-z] [A-Z]``expr "$STRING" : ".\(.*\)"`
echo $STRING
IFSはInternal Field Separatorの略で、値としてはスペースとタブ、改行がIFS変数に セットされています。
IFS変数の値を:
に置き換えて、/etc/passwd
を読み込んでいます。
#!/bin/sh
OLDIFS=$IFS
IFS=:
while read USER PASSWD UID GID GCOS REMAINDER
do
echo "$USER $GCOS"
done < /etc/passwd
IFS=$OLDIFS
ユーザー名とユーザー情報を表示しています。
for i in word-list
do
...
done
inの後に指定した文字列を順番に変数に代入して処理します。
その文字列を区切るのはタブかスペースです。
date
コマンドで挙動を確認します。
date
2021年 3月 6日 土曜日 08時00分59秒 JST
for i in `date`
> do
> echo $i
> done
2021年
3月
6日
土曜日
08時01分39秒
JST
set
コマンドを--
の引数で実行することで、
その後の文字列を順に位置パラメタの1番からセットします。
set -- `date`
☁ 8.3.2 while [ $# -gt 0 ]
> do
> echo $1
> shift
> done
2021年
3月
6日
土曜日
08時08分51秒
JST
awk
はデフォルトではホワイトスペース(スペースやタブ)を区切り文字として考え、
位置パラメタと同じように$nという形で1個1個の文字列をとらえます。
echo "abc def ghi" | awk '{print $2}'
def
,
はスペースとして出力されます。
date | awk '{print $2 $3 $4, $1}'
3月6日土曜日 2021年
,
を表示させる場合は、"
で括ります。
date | awk '{print $2 $3 $4 ", " $1}'
3月6日土曜日, 2021年
awk
コマンドの中の$n
はシェルの位置パラメタとは異なるので、
シェルに解釈されないようにシングルクォートで囲む必要があります。
また、区切り文字はF
オプションで指定できます。
cat /etc/passwd | awk -F: '{print $1 ", " $5}'
5番目から7番目の文字を取り出します。
echo "abc:def:ghi" | cut -c5-7
def
デリミタとして:
を指定して、2番目のフィールドを取得します。
echo "abc:def:ghi" | cut -d':' -f2
def
FILE=/tmp/foo
echo "cannot locate file $FILE" # 変数の展開
cannot locate file /tmp/foo
echo "Errormessage." 1>&2 # エラーメッセージとして標準エラーに書き出したい場合
Errormessage.
echo "This is the first line.
> This is the second line." # 複数行の記述
This is the first line.
This is the second line.
ヒアドキュメントからの入力を標準出力へ書き出す例です。
cat <<- EOF
heredocd then else> This is the first line.
heredocd then else> This is the second line.
heredocd then else> EOF
This is the first line.
This is the second line.
awk
コマンドで位置パラメタの値を任意の文字列の間に表示しています。
echo "abc" | awk '{printf("xxx%syyy\n", $1)}'
xxxabcyyy
ls -l
total 8
-rw-r--r-- 1 jun staff 0 3 6 08:50 aaa
-rwxr-xr-x 1 jun staff 115 3 6 08:50 file_size
cat ./file_size
#!/bin/sh
for FILE in *
do
SIZE=`wc -c < $FILE`
echo $SIZE $FILE | awk '{printf("%5s Bytes %s\n", $1, $2)}'
done
./file_size
0 Bytes aaa
115 Bytes file_size
%5s
という書式は5桁分の枠をとって右づめにする指定です。
カレントディレクトリのファイルサイズを出力しています。
echoコマンドで-n
オプションを指定すると、echoの後に改行しません。
ただし、環境によって挙動が異なるので注意が必要です。
例えばMacOSのbash
では想定通り動作しましたがzsh
、sh
では改行されません。
bash-3.2$ echo -n "Would you like to .... [y/n]? "
Would you like to .... [y/n]? bash-3.2$ # 改行されない
echo -n "Would you like to ... [y/n]? "
read ANSWER
case "$ANSWER" in
y | yes ) FLAG=TRUE ;;
* ) FLAG=FALSE ;;
esac
echo $FLAG
#!/bin/bash
echo -n "Would you like to ... [y/n]? "
stty raw
ANSWER=`dd bs=1 count=1 2> /dev/null`
stty -raw
echo ""
case "$ANSWER" in
[yY] ) FLAG=TRUE ;;
* ) FLAG=FALSE ;;
esac
echo $FLAG
stty
コマンドでraw
モードにすると入出力のバッファ処理を行わず、
入力したデータがそのまま次のプロセスに渡っていきます。
dd
コマンドは標準入力を標準ん出力にそのままの状態でコピーするコマンドです。
入出力のブロックサイズを1(bs=1
)、
入力のうち1ブロックだけを出力(count=1
)ようにしています。
dd
コマンドで出力されるメッセージはゴミ箱に捨てます。
その後、stty
のモードを元に戻しています。
#!/bin/sh
if [ "$1" = "-v" ]; then
VERBOSE=TRUE
shift
fi
if [ "$VERBOSE" = "TRUE" ]; then
echo "Message"
fi
上記のようにすると何かメッセージを出力するときは必ずif文を書かなくてはなりません。
#!/bin/sh
if [ "$1" = "-v" ]; then
VERBOSE=TRUE
shift
fi
if [ "$VERBOSE" = "TRUE" ]; then
ECHO=echo
else
ECHO=:
fi
$ECHO "Message"
上記のように、スクリプトの最初にechoコマンドのオプションがない場合に、 ヌルに置き換えてしまうとif文が不要になります。
tput clear
最近のOSでは単純にclear
でも動作します。
clear
OSによって異なります。
tput bel
echo '\007\c'
stty -echo
と設定するとエコーバックがなくなり、
入力した文字は画面に表示されません。
#!/bin/sh
stty -echo
echo "Enter your password."
read PASSWORD
stty echo
UNIX上で動作しているものは全てプロセスと言います。
プロセスには一意のIDが割り当てられており、 OSha実際にはこの番号で処理しています。
ps
コマンドは現在動作しているプロセスの一覧を表示するコマンドです。
ps -ax | grep php | grep -v grep | awk '{print $1}'
grep検索をしたときに、実際にその名前で動作しているものと、このgrep自身の 引数として
basename
コマンドはパスの最後にあるファイルの名前を出力します。
basename /etc/hosts
hosts
ディレクトリの場合も同じです。
basename /etc
etc
指定したファイルのあるディレクトリ部分だけを取り出す場合はdirname
コマンド
を使います。
dirname /etc/hosts
/etc
dirname /etc
/
CURRENT=`pwd`
cd ~/Downloads
... # 何か実行
... # 何か実行
cd $CURRENT
ファイルの完全パスを得るには、basename
コマンドと組み合わせます。
FULLNAME=`pwd`/`basename echo_back_on_off`
find
コマンドで、directory下のファイルやディレクトリを全て表示します。
find directory -print
指定したディレクトリ下のファイルを表示します。
find . -print
完全パスで出力する場合は以下となります。
find `pwd` -print
-type d
オプションでディレクトリのみ表示します。
find . -type d -print
-type f
オプションでファイルのみ表示します。
find . -type f -print
-name
オプションで指定した文字列と合致するものだけ表示します。
find . -name "aaa" -print
特殊文字も利用でき、"
で囲みます。
find . -name "a*" -print
また!
を付けることで、否定ができます。
find . -type f ! -name "a*" -print
cd SourceDirectory
cp -r . DestinationDirectory
ファイルを新しい日付順に表示するには、ls -t
とオプションを指定します。
もっとも新しいファイルを1つ表示するには、
ls -t aaa bbb ccc | sed -n '1p'
とします。
find
コマンドに-newer
というオプションがあります。
bbb
というファイルがaaa
というファイルより新しい場合の挙動は以下となります。
find aaa -newer bbb -print # 何も出力されない
find bbb -newer aaa -print # ファイル名が出力される
bbb
wc -c file | awk '{print $1}'
wc
は-c
オプションを付けるとそのファイルのバイト数を表示します。
一緒にファイル名も表示するので、awk
コマンドで1番目のフィールドを取り出します。
MacOSでMacintoshHDの容量を取得する場合は、以下となります。
df | sed -n '7p' | awk '{print $5}'
OSごとのdf
ファイルの出力が異なるため、環境ごとにsed
、awk
のコマンドについては
考慮が必要です。