~/Some Bash Script A.sh
+
#
1: 2: 3: 4: 5: 6: 7:
#!/bin/bash
function testFunction() {
echo "Hello, $1!"
}
testFunction "John Smith"
Post: Nekoformi
Date: 2024/02
Bashを効率的に動かしたり可読性を高めるには、幾つかの機能を関数として纏めるべきでしょう。
関数は以下のように記述します。また、ファイルは以下のように実行します。
1: 2: 3: 4: 5: 6: 7:
#!/bin/bash
function testFunction() {
echo "Hello, $1!"
}
testFunction "John Smith"
1: 2:
$ bash ~/"Some Bash Script A.sh"
Hello, John Smith!
#!/bin/bash
)を書くことで機能を明示します。
)が含まれる場合は空白をエスケープ(\
)するか、引用符("
)で囲む必要があります。他のファイルに記述された関数を使用する場合は、source
コマンドでスクリプトを呼び出します。ただし、先程の状態ではtestFunction "John Smith"
が実行されてしまうので、関数用のファイルには関数だけを記述しましょう。
1: 2: 3: 4: 5:
#!/bin/bash
source ~/"Some Bash Script A.sh"
testFunction "Jane Smith"
1: 2:
$ bash ~/"Some Bash Script B.sh"
Hello, Jane Smith!
コンピューターのセットアップでは多くのパッケージをインストールすると思います。それを何度も繰り返す場合は、操作をファイルに纏めたり、その操作すらも関数に纏めます。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
#!/bin/bash
function aptInstall() {
sudo apt -y install "$1"
}
function aptDownload() { # If you need the Advanced Packaging Tool
sudo apt -y --reinstall --download-only install "$1"
}
function snapInstall() {
sudo snap install "$@"
}
function pipInstall() {
pip install "$1"
}
1: 2: 3: 4:
#!/bin/bash
aptInstall curl
snapInstall blender --classic
ファイルやフォルダーを複製したり圧縮ファイルを展開する場合、指定されたディレクトリーが存在しなければ成功しません。それを回避するため、自動的にディレクトリーを作成します。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
#!/bin/bash
function cpFile() {
if [ -d "$2" ]; then
rm -f "$2"
else
mkdir -p "${2%/*}"
fi
echo "Copy file ... $1 -> $2"
cp -fp "$1" "$2"
}
function cpFolder() {
if [ -d "$2" ]; then
rm -rf "${2%/}"/*
else
mkdir -p "${2%/}"
fi
echo "Copy folder ... ${1%/}/ -> ${2%/}/"
cp -RTfp "${1%/}"/ "${2%/}"/
}
function cpZipData() {
if [ -d "$2" ]; then
rm -rf "${2%/}"/*
else
mkdir -p "${2%/}"
fi
echo "Extract file ... $1 -> ${2%/}/"
unzip "$1" -d "${2%/}"/
}
1: 2: 3: 4: 5:
#!/bin/bash
cpFile ~/"Test File.txt" ~/"New File/Copy File.txt"
cpFolder ~/"Test Directory" ~/"New Directory/Copy Directory"
cpZipData ~/"Test Data.zip" ~/"New Data/Copy Data"
${変数名(#|##|%|%%)パターン}
)を用いることでパスを正規化します。rm
コマンドを削除する必要があります。空のファイルを作成する場合はtouch
コマンドが有効ですが、空の文字列をリダイレクション(>
)して作成することも可能です。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
#!/bin/bash
function createEmptyFile() {
echo "" > "$1"
sed '1d' "$1" > "$1"
}
function sudoCreateEmptyFile() {
echo "" | sudo tee "$1" 1> /dev/null
sudo sed '1d' "$1" | sudo tee "$1" 1> /dev/null
}
1: 2: 3:
#!/bin/bash
sudoCreateEmptyFile /root/joke
1: 2: 3: 4: 5: 6: 7: 8: 9:
#!/bin/bash
function insertNewLine() {
echo -e "$2" >> "$1"
}
function sudoInsertNewLine() {
echo -e "$2" | sudo tee -a "$1" 1> /dev/null
}
1: 2: 3:
#!/bin/bash
sudoInsertNewLine /root/joke "Hello, world!"
sed
コマンドを用いて1行目の改行を削除したもの(無)をファイルに書き込みます。sudo
で行えないため、tee
コマンドを使用します。また、インターフェースに出力しないよう1> /dev/null
で結果を虚無に放り込みます。echo
コマンドでは-e
オプションを付与することでエスケープシーケンス(改行を表す\n
等)を解釈します。権限や更新日時を一括で変更したいですか? find
コマンドとxargs
コマンドを駆使しましょう!
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
#!/bin/bash
function changeDefaultPermission() {
echo "Change permission (755) ... $1"
find "$1" -name '*' -print0 | xargs -0 -i sudo chmod 755 {}
find "$1" -name '*' -print0 | xargs -0 -i sudo chown $USER {}
}
function changeMasterPermission() {
echo "Change permission (777) ... $1"
find "$1" -name '*' -print0 | xargs -0 -i sudo chmod 777 {}
find "$1" -name '*' -print0 | xargs -0 -i sudo chown $USER {}
}
function changeTimestamp() {
echo "Change timestamp ($2) ... $1"
find "$1" -name '*' -print0 | xargs -0 -i touch -c -d "$2" {}
}
1: 2: 3: 4: 5:
#!/bin/bash
changeDefaultPermission ~/"Test Directory A"
changeMasterPermission ~/"Test Directory B"
changeTimestamp ~/"Test Directory C" "1970/01/01 00:00:00"
xargs
コマンドは改行や空白を区切りとして処理するため、パスに空白が含まれると正常に動作しません。それを解決するため、find
コマンドでは-print0
オプションを付与することで一覧の区切りをNULL文字(/0
)で記述します。また、xargs
コマンドでは-0
オプションを付与することでNULL文字を区切りとして処理します。スクリプトで対話(ユーザーが入力した文字列を変数に出力)する処理は煩雑なので、一行で実現できるように工夫します。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
#!/bin/bash
function inputData() {
# set +x
local data
echo -e "\e[33;1m$1を入力してください: \e[m" 1>&2
read -p "" data
echo "$data"
# set -x
}
1: 2: 3:
#!/bin/bash
file=$(inputData "ファイル")
local
コマンドの使用が推奨されます。echo
コマンドの内容を出力するため、インターフェースのみに出力する場合はecho
コマンドの結果を標準エラー(1>&2
)で出力する必要があります。対話では選択肢を入出力する状況も想定されます。エラーやミスを防ぐ手段として機能の共通化は最も効果的です。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41:
#!/bin/bash
function selectOption() {
# set +x
local option
local select
eval option=($1)
echo -e "\e[33;1m-Option-------------------------------------------------------------------------\e[m"
local i=1
for item in "${option[@]}"; do
echo -e "\e[33;1m- [${i}] ${item}\e[m"
echo $((i++)) >& /dev/null
done
local label="$2"
if [[ -z "$label" ]]; then
label="オプション"
fi
echo -e "\e[33;1m- [*] キャンセル\e[m"
echo -e "\e[33;1m--------------------------------------------------------------------------------\e[m"
echo -e "\e[33;1m${label}を選択してください: \e[m"
read -p "" select
if [[ $select =~ ^([0-9]{1,2})|([0-1][0-9]{2})|(2[0-4][0-9])|(25[0-9])$ ]]; then # 0 - 255
# set -x
return $((select))
else
# set -x
return 0
fi
}
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
#!/bin/bash
myOption=(
"'Option A'"
"'Option B'"
"'Option C'"
)
selectOption "${myOption[*]}"
option=$?
if [ $option = 0 ]; then
exit
fi
case "$option" in
1 )
echo "Select Option A";;
2 )
echo "Select Option B";;
3 )
echo "Select Option C";;
esac
"'文字列'"
)で囲みます。array[@]
)せずに文字列として参照(array[*]
)します。eval
コマンドで文字列を評価(内容を展開して配列に変換)します。\e[XX;XXm文字列\e[XX;XXm
はANSIエスケープシーケンスです。echo $((変数の演算)) >& /dev/null
で出力結果を虚無に放り込みます。return
で戻り値を出力する場合の制約として、終了ステータス:8ビットの数値(0 - 255)しか出力できないことに注意してください。また、その場合は$?
で終了ステータスを取得します。人間は何かしらのミスを犯します。入力した情報を確認してから処理を実行しましょう。
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
#!/bin/bash
function confirmExecution() {
# set +x
local confirm
echo -e "\e[33;1m本当に実行しますか? (\"Yes\"で実行): \e[m"
read -p "" confirm
case "$confirm" in
Yes|yes )
echo -e "\e[32;1m処理を開始します\e[m"
# set -x
return 1;;
* )
echo -e "\e[31;1m処理を中止します\e[m"
# set -x
return 0;;
esac
}
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
#!/bin/bash
confirmExecution
confirm=$?
if [ $confirm = 0 ]; then
exit
fi
echo "Running..."