Linux Shell
- Linux Shell
- ディレクトリ配下のファイルでループする
- ファイルを一行ずつ読み込んでループする
- forをワンライナーで行う
- 起動引数を使う
- 任意の引数を取得する
- 関数を宣言/使用する
- ディレクトリ/ファイルの存在チェック
- 部分文字列の取得
- タブ区切り(空白区切り)のN番目の要素を取得する
- tsvを正しく読み込む
- シェルの実行ディレクトリを取得する
- ファイルパスから絶対パスを取得する
- ファイルパスからファイル名のみ取得
- ファイルパスからディレクトリ名のみ取得
- .envファイルの読み込み方
- 異なる値のenvファイルを読み込む
- ランダムな文字列を取得する
- 実行年月日時分秒を取得する
- タブを出力する
- 拡張子がほしい/いらない
- 配列
- 連想配列
- 対話式のコマンドを自動化する(expect)
- 変数のスコープ
- pushd/popd
- インデントをつける
- BOMがついているUTF-8ファイルを作成する
- 属性を維持してコピーする
- 環境変数を指定する
- コマンドの戻り値を取る
- 作業ディレクトリを作成する
- 特定の行番号を出力する
- 改行をタブに変換する
- カンマを改行に変換する(CSVを改行区切りにする)
- テストデータを生成する
ディレクトリ配下のファイルでループする
直下のみ
for file in $(pwd)/<対象ディレクトリ>/* ; do
echo ${file}
done
再帰+拡張子指定
for file in `\find . -type f -iname "*.json" ` ; do
echo ${file}
done
ファイルを一行ずつ読み込んでループする
for v in `cat $filename`; do
echo $v
done
過去のログ:その1
while read data; do
echo ${data}
done << END
`cat ${filename}`
END
過去のログ:その1
while read data ; do
echo ${data}
done < <対象ファイル>
注意点
末尾にLFがある行しか読み込まない。(CRLFはNG。)
forをワンライナーで行う
for v in `一覧を返す処理` ; do 結果を使う処理 ; done
# 結果を使う処理内の変数は $v 。
起動引数を使う
シェル
#!/bin/bash
echo $1
echo $2
実行結果
ittimfn@DESKTOP-N4JLN1S:~/cloud9_note/tmp$ sh start.sh hoge piyo
hoge
piyo
任意の引数を取得する
#!/bin/bash
# オプションなしの場合の初期値
aflag=0
bflag=0
bvalue=""
while getopts 'ab:' OPTION; do
case "$OPTION" in
a)
aflag=1
;;
b)
bflag=1
bvalue="$OPTARG"
;;
?)
echo "スクリプトの使い方: $(basename $0) [-a] [-b value]" >&2
exit 1
;;
esac
done
# オプション -a が指定された場合のアクション
if [ "$aflag" -eq 1 ]; then
echo "-a が指定されました。"
fi
# オプション -b が指定され、値が与えられた場合のアクション
if [ "$bflag" -eq 1 ]; then
echo "-b が指定されました。値は ${bvalue} です。"
fi
関数を宣言/使用する
# 関数宣言
# 引数が欲しい場合でも、カッコの中身は空にする。
nanrakano_kansu() {
hikisuu1=$1
hikisuu2=$2
}
# 関数呼び出し
# 呼び出し時にカッコは不要
nanrakano_kansu 'aaa' 'bbb'
ディレクトリ/ファイルの存在チェック
if [-e ${対象ディレクトリ/ファイル} ]; then
# やりたい処理を書く
fi
部分文字列の取得
※WSL2だと動かない。
${変数名:開始位置:取得文字数}
- 変数名に「$」は不要。
- 開始位置は0基底。
シェル
#!/bin/bash
HOGE="abcdef"
# オフセット位置から長さ分を取得
echo ${HOGE:0:2}
# -> ab
echo ${HOGE:2:2}
# -> cd
echo ${HOGE:4:2}
# -> ef
# 長さを省略した場合はオフセットから最後まで出力
echo ${HOGE:2}
# -> cdef
# 長さにマイナスを指定した場合は最後からマイナス分引いた位置までの長さになる
echo ${HOGE:0:-2}
# -> abcd
# オフセットの位置にマイナスを指定した場合は文法として別のパラメータ展開になる(デフォルト値の指定)
# 指定した変数が空文字列の場合は右に指定した文字が入る
echo ${HOGE:-2}
# -> abcdef
HOGE=
echo ${HOGE:-2}
# -> 2
実行結果
ab
cd
ef
cdef
abcd
abcdef
2
部分文字列の取得:参考
タブ区切り(空白区切り)のN番目の要素を取得する
cut
コマンドを使う。
# N番目の要素を取得
pos=1
# 区切り文字がタブの場合は「何で区切るか」は省略可。
cut -f $pos ${tsvファイルパス}
# 区切り文字がタブ以外の場合は-dオプションを使う。
delimiter=' '
cut -d '$delimiter' -f $pos ${ファイルパス}
参考
tsvを正しく読み込む
for
を使うと、行ではなく、要素単位で取得される。
普通にwhile
で回すと、区切り文字が半角スペースに変換されるため、要素に半角スペースを含んでいる場合、うまく処理できない。
#!/bin/bash
filepath=$1
# id, nameは変数名。tsvの要素の1個目と2個目
while IFS=$'\t' read -r id name ; do
echo -e $id '\t' $name
done << END
`cat $filepath`
END
シェルの実行ディレクトリを取得する
`dirname $0`
例:シェルの実行ディレクトリを取得する
#!/bin/bash
echo `dirname $0`
ファイルパス
ittimfn@penguin:~/cloud9_note/tmp$ pwd
/home/ittimfn/cloud9_note/tmp
ittimfn@penguin:~/cloud9_note/tmp$ ls
test.sh
shがあるディレクトリで実行
ittimfn@penguin:~/cloud9_note/tmp$ sh test.sh
.
shより上のディレクトリで実行
ittimfn@penguin:~$ sh ./cloud9_note/tmp/test.sh
./cloud9_note/tmp
shより下のディレクトリで実行
ittimfn@penguin:~/cloud9_note/tmp/tmp2$ sh ../test.sh
..
ファイルパスから絶対パスを取得する
realpath ${filepath}
ファイルパスからファイル名のみ取得
basename ${filepath}
ファイルパスからディレクトリ名のみ取得
dirname ${filepath}
.envファイルの読み込み方
export $(cat .env | grep -v ^# | xargs)
異なる値のenvファイルを読み込む
app.sh
# !/bin/bash
source $1
echo ${env_value}
1.env
env_value="1"
2.env
env_value="2"
実行結果
ec2-user:~/environment/tmp/shell/env $ bash app.sh 1.env
1
ec2-user:~/environment/tmp/shell/env $ bash app.sh 2.env
2
ec2-user:~/environment/tmp/shell/env $
ランダムな文字列を取得する
# hold -w ${桁数}
random_str=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)
実行年月日時分秒を取得する
now=`date '+%Y%m%d_%H%M%S'`
タブを出力する
echo -e "\t"
タブを出力する:参考
拡張子がほしい/いらない
拡張子だけがほしい
for f in *.md ;do
echo $f | sed 's/^.*\.\([^\.]*\)$/\1/'
done
拡張子を除外したファイル名が欲しい
for f in *.md ;do
# ファイル名取得。
echo ${f##*/}
# 拡張子除外
echo ${f%.*}
done
参考
配列
bashで起動すること。
#!/bin/bash
# 重要:shだとエラーになる。bashコマンドの引数として渡すこと。
# 宣言
declare -a sample_list=("a" "b" "c")
# 参照
echo ${sample_list[0]}
echo ${sample_list[1]}
echo ${sample_list[2]}
echo ${sample_list[-1]}
echo ${sample_list[@]}
echo ${sample_list[*]}
echo "----"
# 要素数確認
echo ${#sample_list[@]}
echo ${#sample_list[*]}
echo "----"
# forで参照する。
# 他にも書き方はあるが、Cっぽいのがわかりやすい気がする。
for ((i = 0 ; i < ${#sample_list[@]} ; i++)) {
echo "sample_list[$i] = ${sample_list[i]}"
}
実行結果
a
b
c
c
a b c
a b c
----
3
3
----
sample_list[0] = a
sample_list[1] = b
sample_list[2] = c
参考
連想配列
要はMap。
#!/bin/bash
declare -A user=([id]=1 [name]=$(whoami))
echo ${user[id]}
echo ${user[name]}
$ sh app.sh
1
ec2-user
参考
対話式のコマンドを自動化する(expect)
実行したいコマンドをダブルクオーテーションで囲んで、引数として渡す。
送信するコマンドもダブルクオーテーションで囲む(エスケープする)。
expect -c "
set timeout 5
spawn sftp -r ${user}@${host}
expect \"sftp>\"
send \"cd ${to}\r\"
expect \"sftp>\"
send \"put ${from}\r\"
expect \"sftp>\"
send \"exit\"
"
- spawn
- 別プロセスを起動する。
- expect
- 条件分岐。標準出力でこれが表示されているかを確認する。
- send
- 実行したいコマンド。
試してみた
- sftp_auto_exec_sample:SampleUser0001:Github
- sftp-putを自動化した。
参考
変数のスコープ
app.sh
#!/bin/bash
func() {
echo $HOGE
}
HOGE=hoge
func
echo $HOGE
実行結果
sh app.sh
hoge
hoge
pushd/popd
ディレクトリ移動をスタックで管理する。
- pushd
- 指定したディレクトリをスタックに入れ、そのディレクトリに移動する。
- popd
- スタックからディレクトリを取り出し、そのディレクトリに移動する。
実行例
ec2-user:~/environment/tmp/shell/push_pop $ ls
app.sh directory
ec2-user:~/environment/tmp/shell/push_pop $ tree .
.
├── app.sh
└── directory
├── file01
├── file02
└── file03
1 directory, 4 files
ec2-user:~/environment/tmp/shell/push_pop $ bash app.sh
/home/ec2-user/environment/tmp/shell/push_pop
~/environment/tmp/shell/push_pop/directory ~/environment/tmp/shell/push_pop
file01
file02
file03
~/environment/tmp/shell/push_pop
/home/ec2-user/environment/tmp/shell/push_pop
ec2-user:~/environment/tmp/shell/push_pop $
インデントをつける
- フォーマッタ(拡張機能)をインストール
- shell-format
- 下記キー入力。
OS | Command |
---|---|
Linux | Shift + Ctrl + I |
Windows | Shift + Alt + F |
参考
BOMがついているUTF-8ファイルを作成する
# 上書きされる。
nkf --overrite --oc=UTF-8-BOM ${filepath}
# 上書きされない
nkf --oc=UTF-8-BOM ${filepath} > ${new_filepath}
属性を維持してコピーする
cp -p ${original} ${target}
環境変数を指定する
.bash_profile
にexport付きで宣言する。
コマンドの戻り値を取る
#!/bin/bash
python app.py $1 $2
retval=$?
if [ $retval -eq 0 ]; then
echo "Success"
else
echo "Failure"
fi
作業ディレクトリを作成する
tmpdir=$(mktemp -d)
参考
特定の行番号を出力する
sed -n ${行番号}p ${ファイルパス}
改行をタブに変換する
sed -z 's/\n/\t/g'
カンマを改行に変換する(CSVを改行区切りにする)
cat ${filepath} | tr ',' '\n' | sed -e 's/"//g'
テストデータを生成する
[generate_test_data:SampleUser0001:Github]https://github.com/SampleUser0001/generate_test_data