LoginSignup
14
10

More than 5 years have passed since last update.

bashメモ。

Posted at

メモ

Ubuntu 16.04

最近Bashを少しかじりました(数日程度)。
もう使うことは無いと思いますが、また機会があったときのためのメモです。

多少コマンドが打てること、他のプログラミング言語が扱えることを前提としてます。

シバンの設定は、ここではほとんど省略してます。

ついでに、ある程度複雑なコードになったらVisual Studio Code使ったほうが便利。

基本

viでもGEditでもなんでもいいのでとりあえずファイルを作成

vi sample

こんな感じで記述して保存します。

#!/bin/bash

echo Hello

保存したら実行権限を与えます。

chmod +x sample

実行する

./sample

結果がこちら

Hello

一行目の「#!/bin/bash」はシバンと呼ばれるもので,インタプリタへのパスです。
この例では/bin/bashを指定してます。ノーティラスで/bin/bashを確認してください。

インタプリタはスクリプト言語で書かれたコードを実行するためのプログラム

echoで文字列を表示。
シェルスクリプトではダブルクォートで括る必要がない場合もあります

文字列の扱いとコメント

#!/bin/bash

# ここはコメントです。

echo http://127.0.0.1:80/view?id=3#ccc

echo 10+20-5

echo "Hello World"

echo 'Hello World'

echo "AB\"CD\"EFG" # "の中に"を入れるには\"と書く

echo 'let'"'"'s go'

echo "AB""CD""EFG"

echo 'AB''CD''EFG'
http://127.0.0.1:80/view?id=3#ccc
10+20-5
Hello World
Hello World
AB"CD"EFG
let's go
ABCDEFG
ABCDEFG

まずは、

# ここはコメントです。

「#」の後に書かれる文字列はコメントになり、無視されます。
他の言語では「//」に当たるものです。
ただし、1行目にあるのはシバンですので別の意味を持ちます。

bashの恐ろしいというか、奇妙なところは、以下の例(記号含む)も一応表示されてしまう点です。

echo http://127.0.0.1:80/view?id=3#ccc

一見演算子に見えても

echo 10+20-5

他言語の感覚では「え!?」って感じですよね。
数値や+や-だって文字列で表示してしまいます。

ただ、

; < > | & ( )

などはそのまま使えません。「#」や「$」なども場所によっては問題になります。
他にもありますが面倒なので省きます。後述するエスケープで解説します。

echo "AB\"CD\"EFG"

ダブルクォート中にダブルクォートを埋め込みたいときは、バックスラッシュを使います。環環境によっては円記号のやつです。
エスケープといいます。

ただシングルクォート中はエスケープ出来ないようで、

echo 'let\'s go'

と書くとエラーになります。代わりに

echo 'let'"'"'s go'

と書きます。
見づらいので空白を入れてみましょう。

'let' "'" 's go'

文字列は連結出来ます。

echo "AB""CD""EFG"
echo 'AB''CD''EFG'

エスケープ

echo ABC\(\)\|\<\>\&\$DEF\\G
echo "ABC\(\)\|\<\>\&\$DEF\\G"
echo 'ABC\(\)\|\<\>\&\$DEF\\G'
ABC()|<>&$DEF\G
ABC\(\)\|\<\>\&$DEF\G
ABC\(\)\|\<\>\&\$DEF\\G

クォートで囲まれた場合とそうでない場合でだいぶ結果が違います。
囲んだ場合はバックスラッシュがほとんどそのまま表示されてます。

ダブルクォートとシングルクォートで「$」に違いが出てるのは次で説明します。

以降、シバンは省略します。

変数

str="Hello World"
echo $str

Hello World

str変数に文字列を代入してます。
変数を利用するときは先頭にドル記号を記述します。

注意しないとけいないのは、イコールの両脇に空白を入れるとエラーになる点です。
他言語利用者だとよく引っかかる箇所ですね。

変数を文字列に埋め込む。

str="Hello World"
echo ${str}

name="Yamada"
echo "Hi ${name}!"

Hello World
Hi Yamada!

文字列に代入する際は ${str} などとします。
変数名に他の文字列がくっつかなければ$strでもいいです。

変数のエスケープと展開

str="Hello World"
echo "wq = ${str}"
echo 'wp = ${str}'
echo "esc = \${str}"
wq = Hello World
wp = ${str}
esc = ${str}

シングルクォート中では変数は展開されません。
ダブルクォート中でバックスラッシュにより「$」はエスケープされます。

変数の注意点

a=3
b=5
nbr=${a}+${b}

echo "nbr = ${nbr}"

nbr = 3+5

前回でも紹介しましたが、+や-はそのままでは他言語のように演算子として機能しません。

単純な計算する

a=3
b=5

nbr=`expr $a + $b`
echo "nbr = ${nbr}"

nbr2=$(( $a + $b ))
echo "nbr2 = ${nbr2}"

nbr = 8
nbr2 = 8

計算はexprや$(())などが使えます。
$(())のほうが高機能。

例えば次のコマンドを打った場合、5が表示される。

$ expr 3 + 2

表示ではなく値を取得したい場合は今回のようにバッククォートを使います。
バッククォートで囲んで、中身の式を実行して結果を取得してます。

ヒアドキュメント

「ここからここまで文字列ですー!」というような範囲。
Pがつくスクリプト系言語にはおなじみのやつです。

name="Yamada"

cat << DOC
<h1>Wellcom!!</h1>
Hi. ${name}!
DOC
<h1>Wellcom!!</h1>
Hi. Yamada!

DOCの部分は何でもいいです。
最後のDOCの行には空白など入れてはいけません。警告が出ます。

ヒアドキュメントで変数を展開したくないとき

変数を展開したくない時はDOCをクォートでくくります。

name="Yamada"

cat << 'DOC'
<h1>Wellcom!!</h1>
Hi. ${name}!
DOC
<h1>Wellcom!!</h1>
Hi. ${name}!

ヒアドキュメントから文字列として取得したいときは

name="Yamada"

txt=$(cat <<DOC
<h1>Wellcom!!</h1>
Hi. ${name}!
DOC
)

echo "${txt}"

条件分岐

こちらが参考になります。

文字列の比較、条件に一致した場合。

str1="ABC"
str2="ABC"

# 左右の文字列がが等しい
if [ "$str1" = "$str2" ]; then
    echo "str1 = str2" # 実行される
fi

# 左右の文字列が等しくない
if [ "$str1" != "$str2" ]; then
    echo "str1 != str2" # 実行されない
fi
str1 = str2

変数にはダブルクォートをつける習慣をつけていたほうが無難です。
今回の例ではつける必要ありませんが。
理由はこのページの最後らへんに書いてます。

数値の比較

age=28

# 20未満であればNG
if [ "$age" -lt 20 ]; then
    echo "しっしっ!"
else
    echo "いらっしゃいませ!"
fi
いらっしゃいませ!

数値の比較に(())を使う

age=28

if ((age < 20)); then
    echo "しっしっ!"
else
    echo "いらっしゃいませ!"
fi
いらっしゃいませ!

後で[]とtest、(())とletについて少し触れます。

ループ

for i in {1..10}; do
    echo "i = ${i}"
done
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

1から10まで繰り返します。

ループ:seq

min=1
max=10

for i in $(seq $min $max); do
    echo "i = ${i}"
done

結果は前回と同じです。
seqを使うと変数で範囲を指定出来ます。

ループ:値のリスト

for p in 2 3 5 7 11 13 17; do
    echo "prime = ${p}"
done
prime = 2
prime = 3
prime = 5
prime = 7
prime = 11
prime = 13
prime = 17

ループ:コマンド

for f in $(ls); do
    echo "file = ${f}"
done

lsを使って一覧を表示します。
結果は書きません。

配列

配列いろいろ

arr=(Apple Banana Orange Strawberry) # 配列の初期化。ついカンマつけないよう注意
arr[10]=Grape # indexが10の要素の設定

echo "arr.length = ${#arr[@]}" # 要素数
echo "arr[1] = ${arr[1]}" # indexが1の要素:0から数える
echo "arr[10]" = ${arr[10]} # indexが10の要素
echo "arr = ${arr}" # 先頭の要素
echo "arr = ${arr[@]}" # まとめて表示:@ではなく*でも可

echo ${arr[@]:1:2} # indexが1から2個


arr2=(1 2 3 4 5 6 7 8 9)
echo ${arr2[@]:2:4} #indexが2から4個
arr.length = 5
arr[1] = Banana
arr[10] = Grape
arr = Apple
arr = Apple Banana Orange Strawberry Grape
Banana Orange
3 4 5 6
arr=(Apple Banana Orange Strawberry)

()で配列を初期化してます。
他言語のようについカンマをつけてしまいがちですので注意してください。

arr[10]=Grape
${#arr[@]}

同じことをJavaScriptでやるとlengthで11を取得しますよね、だから何という例ですが。

配列とループ

arr=(Apple Banana Lemon Grap)

for elm in ${arr[@]}; do
    echo $elm
done
Apple
Banana
Lemon
Grap

配列に空白が含まれるときのループの注意点

arr=("A B" "C D" "E F G")

for s in ${arr[@]}; do
  echo "STR = ${s}"
done

for s in "${arr[@]}"; do
  echo "STR2 = ${s}"
done
STR = A
STR = B
STR = C
STR = D
STR = E
STR = F
STR = G
STR2 = A B
STR2 = C D
STR2 = E F G
for s in ${arr[@]}; do

↑これをやってしまうと、要素に空白が含まれていると、その文字列が空白で分割されてしまいます。

for s in "${arr[@]}"; do

↑それが不都合な場合はダブルクォートでくくります。

こちらがとても参考になります。

連想配列

declare -A hmn

hmn["name"]=Yamada
hmn["age"]=30

echo "Name = ${hmn[name]}"
echo "Age = ${hmn[age]}"

Name = Yamada
Age = 30

比較的最近の機能らしいので注意してください

二次元配列・多次元配列もどき

無い、らしいです。
ただ最近は連想配列があるのでそれを使えば楽にできますね。

連想配列が使えない場合は別の方法を用意てそれっぽいことをします。
evalを利用し、変数を動的に操作することでそれっぽいことができるようです。

関数

コマンドなどをまとめておいて後で一気に呼び出すような機能!?

function showMessage()
{
    echo "Hello World"
}

showMessage
Hello World

関数名(showMessage)の名前は自由につけてください。

showMessage

関数を呼び出すと、関数の中身が実行されます。
他言語と違って呼び出す際は()は入りません。
またfunctionは省略出来ます。

showMessage()
{
    echo "Hello World"
}

showMessage

引数

関数にデータを渡すことが出来ます。

function showMessage()
{
    echo "0 = ${0}"
    echo "1 = ${1}"
    echo "2 = ${2}"
    echo "3 = ${3}"
    echo "length = ${#}"
    echo "params = ${@}"
}

showMessage one two three
0 = /home/dicon/dev/vsc/qiita/sample.sh
1 = one
2 = two
3 = three
length = 3
params = one two three

関数内では\$1, \$2 \$3 という名前で引数を取得出来ます。
1から数えます。\$0は見ての通り実行元のシェルのファイル名を取得します。

$@は引数をまとめて表示します。
ただ配列のループはうっかりやりがちのミスにご注意。

function showMessage()
{
    for item in $@; do
        echo "Item = ${item}"
    done

    for item in "${@}"; do
        echo "Item = ${item}"
    done
}

showMessage "zero one" two three
Item = zero
Item = one
Item = two
Item = three
Item = zero one
Item = two
Item = three

関数の戻り値

bashは関数に戻り値は無いようです。

function add()
{
    echo $(($1 + $2))
}

add 3 5
add 101 10
8
111

戻り値っぽいことする場合は$()か``を使います。

function add()
{
    echo $(($1 + $2))
}

v1=$(add 3 5)
v2=$(add 101 10)

echo "V1 = ${v1}, V2 = ${v2}"
V1 = 8, V2 = 111

returnというキーワード

一応bashにもreturnがあります。ただし、他言語とは意味が違います。

function f()
{
    echo "Hello World"
    return 0
}

v=$(f)
echo "V = ${v}"
echo "return = ${?}"
V = Hello World
return = 0

returnは「終了ステータス」というものらしいです。
その値は$?で取得出来ます。

返せる値は0〜255までの符号なし8ビット整数の範囲で、範囲外はANDによって調整されるようです。

0を返せば成功の意味。それ以外(1とか)はエラーの意味。

最後に、復習兼ねて。

バッククォートと$()

p=`pwd`
u=`whoami`
d=`ls -al | grep hell`

echo "pwd = ${p}"
echo "user = ${u}"
echo "------ dir ------"
echo "$d"
pwd = /home/dicon
user = dicon
------ dir ------
-rw-r--r--  1 dicon dicon 12288  9月 29 18:59 .exshell.swp
-rwxr-xr-x  1 dicon dicon    64 10月  3 01:19 cchell
-rwxr-xr-x  1 dicon dicon  6853 10月  4 19:27 drawshell
-rwxr-xr-x  1 dicon dicon  3894 10月  1 01:34 exshell

バッククォートで囲むとコマンドを実行して、その結果を取得します。
これと同じことが$()でも出来ます。

p=$(pwd)
u=$(whoami)
d=$(ls -al | grep hell)

echo "pwd = ${p}"
echo "user = ${u}"
echo "------ dir ------"
echo "$d"

結果はさっきと同じです。

expr

$ expr 1 + 2 + 3
> 6
$ expr 1 \* 2 \* 3
> 6
$ expr \( 2 + 3 \) \* 5
> 25

exprコマンドで簡単な計算をします。 * や () などはエスケープします。
また、数字間、記号間は空けないと構文エラーになったり予想外な結果をだしたりします。

$ expr 1+2+3
> 1+2+3

↑などとするとそのまま「1+2+3」が出力されました

$ expr 1+2 +3
> 構文エラー

↑では構文エラーになりました。


結果を取得するには次のようにします。

$ echo `expr 1 + 2 + 3`
$ echo $(expr 1 + 2 + 3)

letと(())と$(())

letを使って計算が出来ます。インクリメントも出来ます。

let a=3+5-2
echo $a

b=110
let b++
echo "b = ${b}"


let a=2+5-3 b=4*5 c=10/5
echo "a = ${a} b = ${b} c = ${c}"
6
b = 111
a = 4 b = 20 c = 2

(())を使った計算のほうが使いやすい

((a=3+5-2))
echo $a

b=110
((b++))
echo "b = ${b}"

((a=2+5-3, b=4*5, c=10/5))
echo "a = ${a} b = ${b} c = ${c}"
6
b = 111
a = 4 b = 20 c = 2

ところで、exprと違ってletで注意する点は、

$ let aaa=1+2+3; echo $aaa
> 6

↑一列につながってればOK

$ let aaa=1 + 2 + 3; echo $aaa
> 構文エラー

↑つながってないとNG

$ let aaa="1 + 2 + 3"; echo $aaa
> 6

↑クォートでくくればOK

一方、(())の方はその必要なし。

$ ((aaa = 1 + 2 + 3)); echo $aaa
> 6

こちらのほうが使いやすいですね。


$(())で計算結果を取得出来ます。

((a = 1 + 2 + 3 + 4))
echo "a = ${a}"

b=$(( 1 + 2 + 3 + 4 ))
echo "b = ${b}"
a = 10
b = 10

bcを使う・浮動小数点も使える

echo $((5 / 2))
echo $((10 / 3))

echo "-- bc --"
echo "scale=1; 5 / 2" | bc
echo "scale=5; 10 / 3" | bc # 第3まで表示
echo "1 + 2 + 3" | bc
echo "2^8" | bc # 2の8乗
2
3
-- bc --
2.5
3.33333
6
256

letやexprでは浮動小数点数を扱えません。
bcを使うとそれも可能になります。

計算結果を取得するには以下のようにします。

nbr=$(echo "scale=1; 5 / 2" | bc)
echo "nbr = ${nbr}"
2.5

exprよりletのほうが高機能で高速、
bcは浮動小数点数使えて更に高機能

その他、メモ。

ls -alからファイル一覧を表示する。

arr=$(ls -al)
for item in "${arr[@]}"; do
    echo "Item = ${item}\n"
done
IFS_BU=$IFS
IFS=$'\n'

for f in $(ls -al); do
    echo "File = ${f}"
done

IFS=$IFS_BU

bashにおける変数の罠

ここからは未解決&不正確かもしれない内容を含みます(調べるのめんどうで匙投げたため)


未定義を含む引数におけるダブルクォートの有無

function func()
{
    echo "length = ${#}"
    echo "1 = ${1}"
    echo "2 = ${2}"
    echo "3 = ${3}"
    echo "4 = ${4}"
}

unset param1
param2="2"
unset param3
param4="4"

func $param1 $param2 $param3 $param4

echo "==================="

func "$param1" "$param2" "$param3" "$param4"
length = 2
1 = 2
2 = 4
3 = 
4 = 
===================
length = 4
1 = 
2 = 2
3 = 
4 = 4

まずunsetについてですが、明示的に変数を未定義にしてます。
param1やparam3は定義されていないものとしてます。

未定義の変数の存在は無視されてますね。2つしか渡されてません。
でも二回目の呼び出しでは引数が4つあります。


今度はこちらの動作も注意する必要があります。

function func()
{
    echo "length = ${#}"
    echo "1 = ${1}"
    echo "2 = ${2}"
    echo "3 = ${3}"
    echo "4 = ${4}"
}

param1="zero one"
param2=""
param3="three"
param4=""

func $param1 $param2 $param3 $param4

echo "==================="

func "$param1" "$param2" "$param3" "$param4"
length = 3
1 = zero
2 = one
3 = three
4 = 
===================
length = 4
1 = zero one
2 = 
3 = three
4 = 

"zero one"がzeroとoneの2つの引数にわけられていることがわかります。
理由は以下のif例なのかなーと思ってます。


ifの比較で$strがダブルクォート無しだと

str="apple banana"

if [ "apple banana" = $str ]; then
    echo "OK"
else
    echo "NG"
fi

/home/dicon/dev/vsc/qiita/sample.sh: 5 行: [: 引数が多すぎます
NG

警告出ます。結果はNGが表示されました。
*実際にはシバンを省略しているので5行目ではありません。

ダブルクォートなしでエラーになるのはなんでだろう?

str="apple banana"

if [ "apple banana" = apple banana ]; then
    echo "OK"
else
    echo "NG"
fi
/home/dicon/dev/vsc/qiita/sample.sh: 5 行: [: 引数が多すぎます
NG

エラーは同じだし、こういうことでしょうか?
先に空白入りの変数が展開されてしまうために起こるのかなーと(自身はないです)


$strがダブルクォート有りだと

str="apple banana"

if [ "apple banana" = "$str" ]; then
    echo "OK"
else
    echo "NG"
fi
OK

こちらは成功します。

[[]]を使うとダブルクォートする必要はない。

str="apple banana"

if [[ "apple banana" = $str ]]; then
    echo "OK"
else
    echo "NG"
fi
OK

意図通りの結果になります。
空文字の場合も見ておきましょう。

title=""

if [ $title = "" ]; then
    echo "タイトルが空です"
fi
/home/dicon/dev/vsc/qiita/sample.sh: 5 行: [: =: 単項演算子が予期されます
title=""

if [ = "" ]; then
    echo "タイトルが空です"
fi

上と同じエラーになりました。

if [ "$title" = "" ];
if [[ $title = "" ]]

としましょう。よく引っかかりますので注意しましょう。

testと[]

if [ "abc" = "abc" ]; then
    echo "TRUE"
fi
TRUE

[]の正体はtest

if test "abc" = "abc"; then
    echo "TRUE"
fi
TRUE

testは式の評価を終了ステータスで取得可能

$ test "abc" = "abc"; echo $?
> 0

成功すると0

$ test "abc" = "abcd"; echo $?
> 1

失敗すると1(0以外)

||と&&と!

プログラミングでは欠かせない、論理演算子とかを。


# No.1
if [ "a" = "a" ] && [ "b" = "b" ]; then
    echo "No.1 TRUE"
fi

# No.2
if [ "a" = "a" ] && [ "b" = "B" ]; then
    echo "No.2 TRUE"
fi

# No.3
if [ "a" = "a" ] || [ "a" = "B" ]; then
    echo "No.3 TRUE"
fi
No.1 TRUE
No.3 TRUE

&&は左右の結果がともに真(bashの場合は終了ステータス0が真)であれば&&の評価は真となります。

||は左右の結果のどちらかが真であれば||の評価が真になります。

更に詳しく見ていきましょう。

[ "a" = "a" ] && [ "b" = "b" ]; echo $?
[ "a" = "A" ] && [ "b" = "b" ]; echo $?
[ "a" = "a" ] && [ "b" = "B" ]; echo $?
[ "a" = "A" ] && [ "b" = "B" ]; echo $?

echo "========================="

[ "a" = "a" ] || [ "b" = "b" ]; echo $?
[ "a" = "A" ] || [ "b" = "b" ]; echo $?
[ "a" = "a" ] || [ "b" = "B" ]; echo $?
[ "a" = "A" ] || [ "b" = "B" ]; echo $?
0
1
1
1
=========================
0
0
0
1

大文字で書いているところが偽になるようにしてます。
&&は左右が共に真の場合だけ真。
||は左右が共に偽の場合だけ偽。

冒険をしてみよう。


ここからは実験的なものになります。
ここに書いているコードが正しいのかどうか知りません。
調べてません。あくまで遊びで書いてるコードです。


[]はtest、ということは、ifは終了ステータスで分岐してるの?
実験してみよう

ifの評価には関数も行けそうだ。

function trueFunc()
{
    return 0
}

function falseFunc()
{
    return 1
}

if trueFunc; then
    echo "trueFunc = TRUE"
else
    echo "trueFunc = FALSE"
fi

if falseFunc; then
    echo "falseFunc = TRUE"
else
    echo "falseFunc = FALSE"
fi
trueFunc = TRUE
falseFunc = FALSE

終了ステータスを論理値として使えそうな。

次は&&と||の評価のされかたです。

&&は、左の結果が偽であれば右の評価はされません(左が偽だった時点で右の評価をしなくても偽は決定しているため)

||は、左の結果が真であれば右の評価はされません(左が真だった時点で右は評価しなくても真が決定してるため)

その様子を見ていきましょう。

function trueFunc()
{
    echo "TRUE-FUNC"
    return 0
}

function falseFunc()
{
    echo "FALSE-FUNC"
    return 1
}

function putLine()
{
    echo "++++++++++++++++++++"
}

trueFunc && trueFunc; putLine
trueFunc && falseFunc; putLine
falseFunc && trueFunc; putLine
falseFunc && falseFunc; putLine

echo "//////////////////////////////"
echo "//////////////////////////////"

trueFunc || trueFunc; putLine
trueFunc || falseFunc; putLine
falseFunc || trueFunc; putLine
falseFunc || falseFunc; putLine
TRUE-FUNC
TRUE-FUNC
++++++++++++++++++++
TRUE-FUNC
FALSE-FUNC
++++++++++++++++++++
FALSE-FUNC
++++++++++++++++++++
FALSE-FUNC
++++++++++++++++++++
//////////////////////////////
//////////////////////////////
TRUE-FUNC
++++++++++++++++++++
TRUE-FUNC
++++++++++++++++++++
FALSE-FUNC
TRUE-FUNC
++++++++++++++++++++
FALSE-FUNC
FALSE-FUNC
++++++++++++++++++++

&&の場合は、左が偽の時点でそれ以降評価されてません。
||の場合は、左が真の時点でそれ以降評価されてません。
このことが確認できたと思います。

スクリプト系の言語ではこの性質を利用して

[ "a" = "a" ] && echo "if"
[ "a" = "B" ] && echo "else"

echo "==============="

[ "a" = "a" ] || echo "if"
[ "a" = "B" ] || echo "else"
if
===============
else

こんな利用をすることがあります。
具体的には、

unset title

[ -n "$title" ] || title="無題"

echo $title
無題

-nは"$title"が1文字以上あれば真となるオプション。
つまり、文字列が1文字以上なければ、右の式が実行さえる。
bashだとifって結構面倒ですよね。式を短く書くときに利用してます。
これ、いいのかどうかはわかりません。

最後にビックリマーク。

[ "a" = "A" ]; echo $?
! [ "a" = "A" ]; echo $?
1
0

!は真を偽に、偽を真に、つまり反転させます。

letの真偽

if (( 3 < 5 )); then
    echo " 3 < 5 == TRUE"
fi

if (( 3 > 5 )); then
    echo " 3 > 5 == TRUE"
fi
 3 < 5 == TRUE

letは比較演算子が真の時0を、偽の時1を、終了ステータスから得られます。
結果は以下のようになります。

$ (( 3 < 5 )); echo $?
> 0
$ (( 3 > 5 )); echo $?
> 1
$ (( 3 + 3 )); echo $?
> 0
$ (( 3 - 3 )); echo $?
> 1
$ (( 0 )); echo $?
> 1
$ (( 1 )); echo $?
> 0
$ (( -1 )); echo $?
> 0

比較演算子は、真であれば0を、偽であれば1を取得します。
比較しない場合、単に計算結果の場合は、結果が0であれば1を、0以外であれば1になるようです。


こんどは$(())を見ていきます。

$ echo $(( 3 < 5 ))
> 1
$ echo $(( 3 > 5 ))
> 0

$(())による値の取得は、比較演算子に成功すると1を、失敗すると0を返します。
取得しているのは終了ステータスで無い点に注意してください。
真であれば0ではなく1を取得してます。
この辺はこんがらないように。

ではbcコマンドは?

$ echo $( echo " 3 < 5 " | bc);
> 1
$ echo $( echo " 3 > 5 " | bc);
> 0

こちらも同じです。

これらを利用して、

function func()
{
    # $1 >= 50 の結果を反転。
    return $((! ($1 >= 50) ))
}

if func 33; then
    echo "50点以上"
else
    echo "50点未満"
fi


if func 66; then
    echo "50点以上"
else
    echo "50点未満"
fi
50点未満
50点以上

$(())やbcの結果を終了ステータスに利用
真は1を返すので、終了ステータスは!を使って反転させています。

14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10