#
ドキュメント

Document

自分のための備忘録です。

シェル

目次

コマンドの文法

ref. https://qiita.com/ko1nksm/items/9650ed1fc21d668f2732?utm_source=pocket_mylist

$ [プリフィックス]... コマンド名 [サフィックス]...
  • プリフィックス:変数代入 or リダイレクト
  • サフィックス:引数 or リダイレクト

$ 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秒 JST

https://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を参照。

プロセスとシェル

  • コマンド実行からアプリケーション実行まですべての処理がプロセス単位で実行される
  • プロセスとサブシェルを混同しない

同一シェルで複数のプロセスが作成される例を以下に示す。
※サブシェルが作成されるわけではない。

# sample.sh
echo "process"
# グルーピング{}はカレントシェルで実行 
# グルーピング()はサブシェルで実行
{
  while : ;do
    echo "child processs"
  done
}

上記のスクリプトを実行すると 親プロセスと子プロセスの2つのプロセスが作成される。

$ ps auxf | grep bash
ec2-user 13165  0.0  0.4 124992  4188 pts/1    Ss   05:25   0:00          \_ -bash
ec2-user 13227 22.7  0.1 124992  1080 pts/1    S+   05:35   0:05              \_ -bash

サブシェル

(プロセスとは関係なく)以下の処理でサブシェルが作成される。
サブシェルは以下のような特徴を持つ。

  • カレントシェルはサブシェルの実行が終わるまで待機する
  • サブシェルはカレントシェル(親シェル)の環境をすべて引き継ぐ
  • サブシェルの変更はメインシェル(親シェル)に影響しない

入門UNIXシェルプログラミング(p26)

シェルスクリプト実行とサブシェル

  • パイプ|:サブシェルで実行
  • $ sh script.sh:サブシェルで実行
  • $ ./script.sh:サブシェルで実行(実行権限付与済み $ chmod u+x script.sh
  • $ . ./script.sh:カレントシェルで実行
  • $ source script.sh:カレントシェルで実行(.コマンドと異なり相対パスまたは絶対パスで指定する必要がない)
  • execコマンド: $ exec {{command}}

このようにしてコマンドラインからシェルスクリプトを起動すると、 カレントシェルは子プロセスとして もう一つシェルを走らせ、そこでシェルスクリプトを実行します。 この実際にスクリプトを実行するために走るシェルを サブシェル といいます。 子プロセスのサブシェルは親プロセス(カレントシェル)の環境を受け継ぐこと はできますが、その逆はできません。例えば、実行属性を与えたシェルスクリプト を使って環境変数を再設定しようとしても、再設定されるのはサブシェル側の環境変数 であってカレントシェルのものではありません。

https://flex.phys.tohoku.ac.jp/texi/sh/node21.html

カレントシェルで実行した場合とサブシェルで実行した場合の違い

カレントシェルで実行(例 . ./script.sh)

  • . ./script.sh:別プロセスを作成せずにカレントシェルのプロセスでスクリプトが実行されるため、スクリプト実行後にカレントシェルでscript.shで定義された変数を使用できる。

サブシェルで実行(例 sh script.sh)

別プロセスでスクリプトが実行されるため、スクリプト実行後にカレントシェルでsample.shで定義された変数を使用できない。

# script.sh
HELLO=hello
echo $HELLO
$ sh ./script.sh
hello
$ echo $HELLO
// 何も表示されない

シェルスクリプト内のサブシェル

  • ()によるグルーピングはサブシェルで実行
  • {}によるグルーピングはカレントディレクトリで実行

コマンドとシェル組み込み関数

echoやprintfのように、同じ名前で実行ファイルで存在しているコマンド(ex:/bin/echo)と、シェルの組み込みとして存在しているコマンドがあったりする。 シェルスクリプト上ではパスを指定しないと組み込みのものが使われる。機能に差異があったりするので、実行ファイルのほうを使う場合は明示的にパスを指定するなどして区別する。

https://qiita.com/Ping/items/57fd75465dfada76e633

クォート

  • シングルクォート(''):文字列を表す(変数を展開しない)
  • ダブルクォート(""):文字列を表す(変数を展開する)
  • バッククォート(``):中身をコマンドとして実行
    $(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
My_NAME=Hiroshi Sawai
echo MY_NAME
// bash: Sawai: command not found
echo ''
// なにも表示されない
echo 
// シングルクォート内のシングルクォートをエスケープする方法はない
// エスケープできない
echo '\''
>
// 'は表示できない
// 以下のようにシングルクォートの外ではエスケープで表示できる
 $ echo \'foo\'
// foo

$(command)

$(command)はバッククォートと同じようにcommandを実行する。
ただしバッククォートより高機能でネストして使用できる。

$ echo $(date -d @$(date +%s))
// date +%s はUnixタイムスタンプを出力
//  Sat 23 Oct 2021 12:49:17 PM UTC

コマンド実行結果を変数に代入

`{{comand}}`か$({{command}})でコマンド実行結果を変数に代入。

# 引用元:https://qiita.com/Ping/items/57fd75465dfada76e633
TODAY=`date +'%Y/%m/%d'`
echo $TODAY
NOWTIME=$(date +'%H:%M:%S')
echo $NOWTIME

エラーをファイルに出力

$ npm install 2> npm_warning.log

変数

基本

ポイントをまとめると以下のとおりとなる。
  1. = を使う
  2. 値の設定時は変数名の先頭に $ を付けない ($ は参照時のみ)
  3. = の前後にスペースを入れない (前後にスペースがあるとエラー)
  4. 型は存在しない (declare コマンドや typeset コマンドで似たようなことができるが推奨はしない)
  5. 変数名の大文字・小文字は区別される (VAR と var は別物)

特に「2」は初心者が犯しがちなミスなので注意すること。

変数を使用する | UNIX & Linux コマンド・シェルスクリプト リファレンス

特殊変数

$0 # シェルスクリプト名
$1~$9 # 1番目~9番目の引数
$* # すべての引数(詳細後述)
$@ # すべての引数(詳細後述)
$# # 引数の数
$? # 直前に実行したコマンドの終了ステータス。0は成功、0以外は失敗
$- # シェルの実行オプション (/bin/bash -opt)
$$ # シェルのプロセスID
$! # 最後に実行したバックグランドプロセスのプロセスID
$_ # 最後に実行したコマンドの最後の引数

引用元:https://www.tohoho-web.com/ex/shell.html#special

環境変数の例 PATH

変数PATHの値を確認します。

$ echo ${PATH}

.bash_profile

パスを通すとは、シェル設定ファイル(.bash_profile.zshrcで、環境変数PATHにパスを追加することです。   exportで環境変数(この場合はPATH)を更新します。

PATH=$PATH:/path/to/add
export PATH

PATHのTIPS

(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

シェル変数と環境変数とシェルスクリプト

  • シェル変数は子プロセスのシェルスクリプトには引き継がない
  • 環境変数は子プロセスのシェルスクリプトでも有効
    環境変数を定義するにはexportを使用する
#!/bin/bash

# script.sh
# このスクリプトはシェル変数が子プロセスでは使用できないことを確認します。
# 実行前にシェルからBAR=BAZとシェル変数を定義してください。
# 次に sh script.sh で子プロセスとして実行してください。

echo '子プロセス'
echo $BAR
$ BAR=BAZ
$ sh script.sh # 子プロセスで実行
子プロセス
  <-- $BARは表示されない

環境変数

# 以下では環境変数として設定されない実装がある(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とほぼ同等)

シェル変数

シェル変数はsetコマンドで一覧表示する。

$ set
// すべてのシェル変数を表示
$ Foo="Bar"
// シェル変数定義
$ set | grep Foo
// Foo=Bar
$ printenv | grep Foo
// 結果なし
$ export Foo
$ printenv | grep Foo  # 環境変数になる
// Bar

ref.
https://qiita.com/chihiro/items/bb687903ee284766e879

if文

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

[ ... ]はtestの糖衣構文。...の前後に半角スペースが必要。

[[ ... ]]はbash専用の言語構造で[ ]より高機能。

[ ... ] で使用できる表記。

( 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

for in

#!/bin/bash
total=0
for x in 100 200 300
do
    total=$(expr $total + $(expr $x \* 2))
done
echo $total
#!/bin/bash
total=0
for x in 100 200 300
do
    total=`expr $total + \`expr $x \* 2\``
done
echo $total

while/read

// input.txt
abc
def
ghi
while read LINE
do
  echo $LINE
done < input.txt

set -eu

https://qiita.com/youcune/items/fcfb4ad3d7c1edf9dc96

サンプル集

ディレクトリのファイル一覧を取得

#!/bin/bash
for f in `find . -maxdepth 1 -type f`
do
    echo $f
done

ディレクトリ内のファイルサイズを取得

#!/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 $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

expr

#!/bin/bash
total=0
for x in 100 200 300
do
    total=`expr $total + \`expr $x \* 2\``
done
echo $total

Ref