"$var"
$(command)
は必ずダブルクォートで囲む "$(commnad)"
本ドキュメントは上記に準拠していません。
ref. https://qiita.com/ko1nksm/items/9650ed1fc21d668f2732?utm_source=pocket_mylist
# コマンドはプリフィックスの0回以上の繰り返し コマンド名 サフィックスの0回以上の繰り返しで構成される
$ [プリフィックス]... コマンド名 [サフィックス]...
変数代入はコマンドに環境変数を渡す処理です。
※ 例は env
コマンドを省略していますが、すべての環境で省略できるわけではありません( Mac の Zsh
や Bash
では省略可能)。
$ date
2021年 11月 8日 月曜日 20時00分26秒 JST$ LANG=C date
Mon Nov 8 20:01:54 JST 2021$ date
2021年 11月 8日 月曜日 20時03分17秒 JSThttps://qiita.com/ko1nksm/items/9650ed1fc21d668f2732?utm_source=pocket_mylist
LANG=C date # 一時的な変数代入
# 厳密にはシェル関数の場合は・・・と長い話があるのですが省略します
LANG=C # 恒久的な変数代入https://qiita.com/ko1nksm/items/9650ed1fc21d668f2732?utm_source=pocket_mylist
※ Linuxのローカライゼーション系LANG変数:langについて
$ > /tmp/ls.txt la -al
# 以下と等価
$ ls -al > /tmp/ls.txt
詳細はlinuxを参照。
サブシェルとは、シェル (sh や csh など) から起動された子プロセスのシェルのこと。
ref. http://x68000.q-e-d.net/~68user/unix/pickup?%A5%B5%A5%D6%A5%B7%A5%A7%A5%EB
サブシェルはシェルから起動した子プロセスのことです。
サブシェルの特徴をまとめます。
シェル変数
を引き継がないシェル変数
はカレントシェル(親シェル)に影響しない環境変数
をを引き継ぐ環境変更
はカレントシェル(親シェル)に影響しない環境変数
をサブシェルで変更してもカレントシェルの環境変数
は変更されないref. 入門UNIXシェルプログラミング(p26)
カレントシェルでスクリプトを実行するか、サブシェルで実行するかはスクリプトの実行方法によってきまります。
|
:サブシェルで実行$ sh script.sh
:サブシェルで実行$ ./script.sh
:サブシェルで実行(実行権限付与済み $ chmod u+x script.sh
)$ . ./script.sh
:カレントシェルで実行$ source script.sh
:カレントシェルで実行( .
コマンドと異なり相対パスまたは絶対パスで指定する必要がない)exec
コマンド: $ exec {{command}}
:不明子プロセスを作成せずにカレントシェルのプロセスでスクリプトを実行します。
スクリプト実行後にカレントシェルで script.sh
で定義した変数(環境変数
、シェル変数
とも)を使用できます。
子プロセスでスクリプトを実行します。そのためスクリプト実行後に、カレントシェルで script.sh で定義した変数(環境変数
、シェル変数
とも)を使用できません。
# script.sh
hello="hello"
echo "$hello"
$ sh ./script.sh
hello
$ echo "$hello"
// 何も表示されない
type
コマンドはコマンドの種類を表示します。
a
オプション:一致したすべてを表示する
$ type -a echo
echo is a shell builtin
echo is /bin/echo
コマンドの種類は以下のとおりです。
ポイントをまとめます。
- = を使う
- 値の設定時は変数名の先頭に $ を付けない ($ は参照時のみ)
- = の前後にスペースを入れない (前後にスペースがあるとエラー)
- 型は存在しない (declare コマンドや typeset コマンドで似たようなことができるが推奨はしない)
- 変数名の大文字・小文字は区別される ( VAR と var は別物)
特に「2」は初心者が犯しがちなミスなので注意すること。
ref. 変数を使用する | UNIX & Linux コマンド・シェルスクリプト リファレンス
$0 # シェルスクリプト名
$1~$9 # 1番目~9番目の引数
$* # すべての引数(詳細後述)
$@ # すべての引数(詳細後述)
$# # 引数の数
$? # 直前に実行したコマンドの終了ステータス。0は成功、0以外は失敗
$- # シェルの実行オプション (/bin/bash -opt)
$$ # シェルのプロセスID
$! # 最後に実行したバックグランドプロセスのプロセスID
$_ # 最後に実行したコマンドの最後の引数
引用元:https://www.tohoho-web.com/ex/shell.html#special
# 以下では環境変数として設定されない実装がある( Mac の zsh では環境変数として設定される)
$ FOO=BAR
$ export FOO
$ env | grep FOO
// 空
# 以下で環境変数として設定される
$ export FOO=BAR
$ env | grep FOO
// FOO=BAR
# 以下でも環境変数として設定される
$ FOO=BAR export FOO
$ env | grep FOO
// FOO=BAR
$ unset FOO
printenv
コマンド環境変数を表示します。
// 環境変数一覧を表示
$ printenv
// 指定した環境変数の値を表示
$ printenv SHELL
/bin/bash
env
コマンドを引数なしで実行しても環境変数一覧を取得できます。
$ env
// 環境変数一覧を表示( printenv とほぼ同等)
env
本来のユースケースは、コマンド実行時に一時的に環境変数をコマンドに渡すことです。
// date
// 2022年 1月22日 土曜日 10時25分27秒 JST <--- 変更前
$ env LANG=c date
Sat Jan 22 10:25:55 JST 2022 <---- 一時的に変更
// 一時的に環境変数を設定するのでコマンド実行ごはもとに戻る
// $ date
// 2022年 1月22日 土曜日 10時26分06秒 JST <--- もとに戻る
Zsh や Bash では env
は省略可能ですが独自使用なので注意が必要です。
# これでも大丈夫
$ LANG=c date
Sat Jan 22 10:25:55 JST 2022
シェル変数は set
コマンドで一覧表示する。
$ set
// すべてのシェル変数を表示
$ Foo="Bar"
// シェル変数定義
$ set | grep Foo
// Foo=Bar
$ printenv | grep Foo
// 結果なし
$ export Foo
$ printenv | grep Foo # 環境変数になる
// Foo=Bar
ref.
https://qiita.com/chihiro/items/bb687903ee284766e879
echoやprintfのように、同じ名前で実行ファイルで存在しているコマンド(ex:/bin/echo)と、シェルの組み込みとして存在しているコマンドがあったりする。 シェルスクリプト上ではパスを指定しないと組み込みのものが使われる。機能に差異があったりするので、実行ファイルのほうを使う場合は明示的にパスを指定するなどして区別する。
ref. https://qiita.com/Ping/items/57fd75465dfada76e633
シェルのワイルドカードと正規表現やプログラム言語固有のワイルドカードの違いに把握します。
*
:任意の0以上の文字列(0文字以上でも良いことに注意)?
:任意の1文字$ echo *
// カレントディレクトリのファイルを表示する
README.html test1.txt test2.txt
$ echo "*"
// * が表示(glob 機能が無効化される)
''
):文字列を表す(パラメータ(変数)を展開しない)""
):文字列を表す(パラメータ(変数)を展開する)$(command)
も command を実行する。$(command)
のほうが視認性もよくネストも可能なので、通常 $(command)
を使用する文字 | 内容 |
---|---|
' | すべてキャラクタがエスケープされます。 |
" | $ で変数を展開します。$ , `,\, "はエスケープする必要があります。 |
$ nowTime=2021-01-01
$ echo '時刻:${nowTime}'
// 時刻:${nowTime}
$ echo "時刻:${nowTime}"
// 時刻:2021-01-01
$ echo "時刻:`date`"
// 時刻:Sat 23 Oct 2021 12:38:37 PM UTC
$ echo "時刻:$(date)"
// 時刻:Sat 23 Oct 2021 12:38:37 PM UTC
echo Hello World
// Hello World # クォートで囲まなくても表示
echo 'Hello' World
// Hello World # 文字列の連結に演算子は必要ない
MY_NAME='Hiroshi Sawai'
echo $MY_NAME
// Hiroshi Sawai
echo ''
// なにも表示されない
echo
// シングルクォート内のシングルクォートをエスケープする方法はない
// エスケープできない
echo '\''
quote>
// 'は表示できない
// 以下のようにシングルクォートの外ではエスケープで表示できる
$ echo \'foo\'
// foo
$(command)
はバッククォートと同じように command を実行する。
ただしネストできるなどバッククォートより高機能。
$ echo $(date -d @$(date +%s))
// 数字の前に @ を付けると、UNIX時間での指定になる。ref. https://hydrocul.github.io/wiki/commands/date.html
// date +%s はUnixタイムスタンプを出力
// Sat 23 Oct 2021 12:49:17 PM UTC
`{{command}}`か$({{command}})
で実行した結果を変数に代入します。
# 引用元:https://qiita.com/Ping/items/57fd75465dfada76e633
TODAY= "$(date +'%Y/%m/%d')"
echo "$TODAY"
NOWTIME="$(date +'%H:%M:%S')"
echo "$NOWTIME"
環境変数 PATH
の値を確認する。
$ echo "$PATH"
パスを通すとは、シェル設定ファイル( .bash_profile
や .zshrc
) で環境変数 PATH
にパスを追加すること。
export
で環境変数(この場合は PATH
)を更新する。
PATH=$PATH:/path/to/add
export PATH
(1) PATH
の優先順位を確認
$ cat /etc/paths
// 優先度の高い順に表示
例えば以下のように、 .bash_profile
で優先順位を変更できます。
PATH=/path/to/dir:$PATH
export PATH
(2) source コマンド
.bash_profile
を変更したときは、ログインし直さないと変更が反映されません。source
コマンドを使うとログインし直さなくても変更が反映されます。.
コマンドは source
コマンドとほぼ等価です.
$ source /path/to/.bash_profile
// or
$ . /path/to/.bash_profile
本当に正しい .bashrc と .bash_profile の使ひ分け - Qiita
#!/bin/bash
#
# e エラー発生時にスクリプトを終了する
# u 未定義変数の使用をエラーにする
set -eu
var="abc"
# test command
if test "$var" = "abc"; then
echo "same"
fi
# [ command
echo "=== [ command ==="
if [ "$var" = "abc" ]; then
echo "same"
fi
# [[ keyword
# [[ keyword は Bash の独自拡張
# 正規表現 =~ などの機能が使用できる
echo "=== [[ keyword ==="
if [[ "$var" =~ ^a.+ ]]; then
echo "match"
fi
# ( ) command substitution
echo "=== ( ) command substitution ==="
echo -e "aaa\nbbb\ccc" >tmp.txt
if (grep "bbb" tmp.txt >/dev/null); then
echo "match"
fi
# (( )) arithmetic operation
echo "=== (( )) arithmetic operation ==="
num=5
if ((num < 10)); then
echo "5 is less than 10."
fi
( exp ) # exp をグルーピング
! exp # exp が偽であれば
exp1 -a exp2 # exp1 かつ exp2 であれば
exp2 -o exp2 # exp1 または exp2 であれば
str1 = str2 # 文字列 str1 と str2 が等しければ
str1 != str2 # 文字列 str1 と str2 が等しくなければ>
-z str # 文字列 str が0文字であれば(zero)
-n str # 文字列 str が0文字で以上であれば(not zero)
str # 文字列 str が0文字で以上であれば(-n str と同じ)
num1 -eq num2 # 数値 num1 が num2 と等しければ(equal)
num1 -ne num2 # 数値 num1 が num2 異なっていれば(not equal)
num1 -ge num2 # 数値 num1 が num2 以上であれば(grater than or equal)
num1 -gt num2 # 数値 num1 が num2 より大きければ(grater than)
num1 -le num2 # 数値 num1 が num2 以下であれば(less than or equal)
num1 -lt num2 # 数値 num1 が num2 より小さければ(less than)
file1 -ef file2 # ファイル file1 が file2 と同一実体であれば(equal file)
file1 -nt file2 # ファイル file1 が file2 より新しければ(newer than)
file1 -ot file2 # ファイル file1 が file2 より古ければ(older than)
-e file # ファイル file が存在していれば
-s file # ファイル file が0バイト以上であれば
-f file # ファイル file がレギュラーファイルであれば
-r file # ファイル file が読み込み可能であれば
-w file # ファイル file が書き込み可能であれば
-x file # ファイル file が実行可能(ディレクトリの場合は移動可能)であれば
-d file # ファイル file がディレクトリであれば
-h file # ファイル file がシンボリックリンクファイルであれば(-Lと同義)
-L file # ファイル file がシンボリックリンクファイルであれば(-hと同義)
-b file # ファイル file がブロックデバイスファイルであれば
-c file # ファイル file がキャラクタデバイスファイルであれば
-p file # ファイル file が名前付きパイプであれば
-S file # ファイル file がソケットファイルであれば
-k file # ファイル file にスティッキービットが設定されていれば(chown o+t)
-u file # ファイル file にセットユーザIDビットが設定されていれば(chown u+s)
-g file # ファイル file にセットグループIDビットが設定されていれば(chown g+s)
-O file # ファイル file が実効ユーザIDに所有されていれば
-G file # ファイル file が実効グループIDに所有されていれば
-t fd # ファイルディスクリプタ fd がターミナルとして開かれていれば
引用元:https://www.tohoho-web.com/ex/shell.html#if-statemant
DB="mysql";
if [[ "$DB" == "mysql" ]]; then
mysql -u root myapp_test < "${TRAVIS_BUILD_DIR}/bin/infotownlinkwp.sql";
fi
if [[ "$DB" == "pgsql" ]]; then
psql myapp_test < "${TRAVIS_BUILD_DIR}/bin/infotownlinkwp.sql";
fi
#!/bin/bash
total=0
for x in 100 200 300; do
((total = total + ("$x" * 2)))
done
echo $total
# expr is antiquated. (()) is best practice.
total=0
for x in 100 200 300; do
total=$(expr "$total" + "$(expr "$x" \* 2)")
done
echo "$total"
// input.txt
abc
def
ghi
while read line
do
echo "$line"
done < input.txt
ファイル・ディスクリプター番号 | 入出力先 |
---|---|
0 | 標準入力 |
1 | 標準出力 |
2 | 標準エラー出力 |
n | 任意の入出力先 |
ref. https://qiita.com/laikuaut/items/e1cc312ffc7ec2c872fc
output.sh
#!/bin/bash
# e エラー発生時にスクリプトを終了する
# u 未定義変数の使用をエラーにする
set -eu
echo 'standard output' >&1
echo 'standard error output' >&2
exit 1
※ スクリプトを実行するときは chmod u+x で実行権限を付与する。またサブシェルで実行しないとシェル自体が終了する(例 . path/to/script )
ディスクリプタが省略された場合は標準出力 1
を指定したことになる。
$ ./output.sh 1>/dev/null
standard error output // <-- 標準エラーのみ表示。標準出力は /dev/null に出力。
// 以下と等価
$ ./output.sh >/dev/null
standard error output
& のみを指定した場合は標準出力 1
と標準エラー出力 2
を指定したことになる。
$ ./output.sh &>/dev/null
// 出力なし
リダイレクトを複数指定した場合は左から右に評価される。
$ ./output.sh 2>&1 >/dev/null
// standard error output
// 以下は↑と等価
$ ./output.sh 2>&1 1>/dev/null
標準出力 1
および標準エラー出力 2
ともに /dev/null
に出力。
$ ./output.sh >/dev/null 2>&1
// 出力なし
e
: エラー発生時にスクリプトを終了するu
: 未定義変数の使用をエラーにするhttps://qiita.com/youcune/items/fcfb4ad3d7c1edf9dc96
ディレクトリのファイル一覧を取得。
#!/bin/bash
for f in $(find . -maxdepth 1 -type f)
do
echo "---"
echo $f
done
※ "$(find . -maxdepth 1 -type f)" だと以下のように 1 度しかループされない。
ディレクトリ内のファイルサイズを取得。
#!/bin/bash
for f in $(find . -maxdepth 1 -type f)
do
wc -c < "$f"
done
ディレクトリサイズ表示。
#!/bin/bash
for d in $(find . -maxdepth 1 -type d)
do
du -h "$d"
done
文字列結合
#!/bin/bash
lines=''
while read line
do
lines=$lines$line
done < file.txt
echo $lines
file.txt
aaa
bbb
ccc
実行
$ ./script.sh
aaabbbccc