cloud9_note

cloud9に限らないメモ

View on GitHub

Linux Shell

ディレクトリ配下のファイルでループする

直下のみ

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だと動かない。

${変数名:開始位置:取得文字数}

シェル

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

部分文字列の取得:参考

Qiita:bashで変数から部分文字列を取得する

タブ区切り(空白区切り)の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\"
"

試してみた

参考

変数のスコープ

app.sh

#!/bin/bash

func() {
    echo $HOGE
}

HOGE=hoge
func
echo $HOGE

実行結果

sh app.sh 
hoge
hoge

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 $ 

インデントをつける

  1. フォーマッタ(拡張機能)をインストール
    • shell-format
  2. 下記キー入力。
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