#【Shell开发】笔记&技巧
注:以下命令,暂且仅在 MacOS 上验证有效;可查看「原文」获取最新内容。
常用变量&常量
#!/usr/bin/env bash
readonly TIMESTAMP=$(date +%s) # 当前时间戳,eg. 1617351251
readonly DATE=$(date -r "$TIMESTAMP" "+%Y-%m-%d %H:%M:%S") # 当前时间,eg. 2021-04-02 16:14:11
readonly DATE_STAMP=$(date -r "$TIMESTAMP" "+%Y%m%d%H%M%S") # 当前时间戳,eg. 20210402161411
readonly CURRENT_PATH=$(pwd) # 当前所在路径
readonly SCRIPT_DIRPATH=$(dirname "$0") # 当前脚本的文件路径
readonly SCRIPT_BASENAME=$(basename "$0") # 当前脚本的文件名
readonly SCRIPT_BASENAME_WITHOUT_SUFFIX=${SCRIPT_BASENAME%.*} # 文件名(不含后缀)
readonly SCRIPT_BASENAME_SUFFIX=${SCRIPT_BASENAME##*.} # 文件后缀
基本操作
默认值
返回默认值
#!/usr/bin/env bash
value=${1:-'默认值'}
echo "value = $value"
# $ sh shell.sh
# value = 默认值
# $ sh shell.sh hello
# value = hello
赋值默认值
#!/usr/bin/env bash
echo "value = ${value:='默认值 1'}" # 此时 $value 已被赋值
echo "value = ${value:='默认值 2'}" # 再次输出,已为 '默认值 1'
# $ sh shell.sh
# value = '默认值 1'
# value = '默认值 1'
算数运算
#!/usr/bin/env bash
a=$((60 * 5))
echo "a = $a"
三元表达式
#!/usr/bin/env bash
a=50
b=10
# 判断并输出
[ $a -gt $b ] && echo "a > b" || echo "a <= b"
# 等价于
# if [ $a -gt $b ]; then echo "a > b"; else echo "a <= b"; fi;
# 判断并赋值
which_is_bigger=$([ $a -gt $b ] && echo "a" || echo "b")
echo "which is bigger: $which_is_bigger"
遍历数组
#!/usr/bin/env bash
list=(
"aaa"
"bbb"
"ccc"
)
for item in ${list[@]}; do
echo "$item"
done
截取子串
#!/usr/bin/env bash
file="image.jpg"
echo "名称为:" ${file%.*}
echo "后缀为:" ${file#*.}
输出相关
带颜色输出
#!/usr/bin/env bash
echoDebug() { echo "\033[1;2m$@\033[0m"; } # 调试信息,灰色
echoVerbose() { echo "\033[1;2m$@\033[0m"; } # 冗余信息,灰色
echoInfo() { echo "\033[1;36m$@\033[0m"; } # 提示信息,蓝色
echoSuccess() { echo "\033[1;32m$@\033[0m"; } # 成功信息,绿色
echoWarn() { echo "\033[1;33m$@\033[0m"; } # 警告信息,黄色
echoError() { echo "\033[1;31m$@\033[0m"; } # 错误信息,红色
echoFatal() { echo "\033[5;31m$@\033[0m" && exit 1; } # 严重错误 并终止程序,红色闪烁
忽略输出
#!/usr/bin/env bash
# 用法:command >/dev/null 2>&1
# 示例
git fetch >/dev/null 2>&1
桌面通知(MacOS)
#!/usr/bin/env bash
osascript -e "display notification \"\" with title \"这是一条桌面通知\" subtitle \"这是桌面通知的子标题\""
输入相关
命令行 OPT 参数解析
#!/usr/bin/env bash
if [[ $1 && ${1:0:2} != "--" ]]; then
while getopts "dvh" OPT; do
case $OPT in
d) debug="1" && verbose="1" ;;
v) version="1" ;;
h) help="1" ;;
?) help && exit 1 ;;
esac
done
shift $((OPTIND - 1))
fi
读取命令行输入
#!/usr/bin/env bash
read -n1 -p "Do you want to continue [Y/N]? " answer
case $answer in
Y | y)
echo "\nFine, continue";;
N | n)
echo "\nOk, bye~";;
*)
echo "\nError choice";;
esac
参数个数判断
#!/usr/bin/env bash
if [ $# -eq 0 ]; then
echo "没有参数"
else
echo "有 $# 个参数"
fi
MacOS 右键的“快速操作”
打开终端,自动执行指定命令,并传入所选文件
tell application "Finder" to set _selection to selection
tell application "Terminal"
# 所有所选路径
set _pathsStr to ""
repeat with n from 1 to (count of _selection)
set _pathsStr to _pathsStr & POSIX path of ((item n of _selection) as text) & " "
end repeat
# 执行命令
if (count of windows) is not 0 then
do script "~/Library/Services/test.sh " & quoted form of _pathsStr in window 1
else
do script "~/Library/Services/test.sh " & quoted form of _pathsStr
end if
activate
end tell
判断相关
判空
#!/usr/bin/env bash
value=''
if [ -z $value ]; then
echo "value 为空";
else
echo "value = $value";
fi
执行失败时 输出提示
#!/usr/bin/env bash
path="/path/that/does/not/exist"
cd "$path" || ! echo "前往目录失败($?):$path" # 执行失败时 输出提示
cd "$path" || ! echo "前往目录失败($?):$path" || exit 1 # 执行失败时 输出提示,并退出
echo "当前路径:$(pwd)"
忽略系统错误:
#!/usr/bin/env bash
path="/path/that/does/not/exist"
cd "$path" >/dev/null 2>&1 || ! echo "前往目录失败($?):$path" # 执行失败时 输出提示
cd "$path" >/dev/null 2>&1 || ! echo "前往目录失败($?):$path" || exit 1 # 执行失败时 输出提示,并退出
echo "当前路径:$(pwd)"
模糊判断
#!/usr/bin/env bash
res="upload success"
if [[ $res == *"success"* ]]; then
echo "成功了";
else
echo "失败了";
fi
判断是文件/文件夹
#!/usr/bin/env bash
path_1="/Users/$USER"
if [ -d $path_1 ]; then
echo "$path_1 是个文件夹"
else
echo "$path_1 不是文件夹"
fi
path_2="/Users/$USER/.bashrc"
if [ -f $path_2 ]; then
echo "$path_2 是个文件"
else
echo "$path_2 不是文件"
fi
判断文件是否存在
path="/Users/$USER/xx"
if [[ -e "$path" ]]; then
echo "文件存在"
else
echo "文件不存在"
fi
版本比较
实现1
#!/usr/bin/env bash
# version_gt 判断$1是否大于$2
version_gt() {
[[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -gt "${2#*.}" ]]
}
# version_ge 判断$1是否大于等于$2
version_ge() {
[[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -ge "${2#*.}" ]]
}
# version_lt 判断$1是否小于$2
version_lt() {
[[ "${1%.*}" -lt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -lt "${2#*.}" ]]
}
示例:
if version_gt "$macos_version" "10.14"; then
echo "$macos_version"
else
echo '\033[1;31m检测到你不是最新系统,会有一些报错,请稍等Ruby下载安装;\033[0m
'
fi
实现2
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
文件操作
写文件
#!/usr/bin/env bash
file="/Users/$USER/Desktop/temp.txt"
echo "$(date +%s)" >> "$file" # 覆盖内容(文件不存在时 会自动创建)
echo "$(date +%s)" >> "$file" # 追加内容(文件不存在时 会自动创建)
文件的修改时间
#!/usr/bin/env bash
path="/Users/$USER/.bashrc"
timestamp=$(stat -f %c "$path")
echo "$path 的最近修改时间为 $timestamp (时间戳)"
遍历文件夹
#!/usr/bin/env bash
path="/Users/$USER/*"
for sub_path in $path; do
echo "$sub_path";
done
查找文件
#!/usr/bin/env bash
dir="/Users/$USER/Desktop"
find "$dir" -name "*.txt" -type f
查找文件 并复制到指定目录
#!/usr/bin/env bash
find_dir="/Users/$USER/Desktop"
bak_dir="/Users/$USER/Desktop/bak"
find "$find_dir" -name "*.txt" -type f -exec cp {} "$bak_dir" \;
清空文件夹
#!/usr/bin/env bash
# 删除后重建
rm -rf "$dir" && mkdir -p "$dir"
压缩文件
#!/usr/bin/env bash
file="/Users/$USER/Desktop/bak" # 源文件
zip_file="/Users/$USER/Desktop/bak" # 压缩后的文件
# -q 不显示打包过程
# -r 包括子目录
# -j 不处理压缩文件中原有的目录路径
zip -qrj "$zip_file" "$file" || ! echo "文件压缩失败($?)" || exit 1
图片处理
MacOS 的 sips
$ sips
sips - scriptable image processing system.
This tool is used to query or modify raster image files and ColorSync ICC profiles.
Its functionality can also be used through the "Image Events" AppleScript suite.
Usages:
sips [image-functions] imagefile ...
sips [profile-functions] profile ...
Profile query functions:
-g, --getProperty key
-X, --extractTag tag tagFile
--verify
-1, --oneLine
Image query functions:
-g, --getProperty key
-x, --extractProfile profile
-1, --oneLine
Profile modification functions:
-s, --setProperty key value
-d, --deleteProperty key
--deleteTag tag
--copyTag srcTag dstTag
--loadTag tag tagFile
--repair
-o, --out file-or-directory
Image modification functions:
-s, --setProperty key value
-d, --deleteProperty key
-e, --embedProfile profile
-E, --embedProfileIfNone profile
-m, --matchTo profile
-M, --matchToWithIntent profile intent
--deleteColorManagementProperties
-r, --rotate degreesCW
-f, --flip horizontal|vertical
-c, --cropToHeightWidth pixelsH pixelsW
--cropOffset offsetY offsetH
-p, --padToHeightWidth pixelsH pixelsW
--padColor hexcolor
-z, --resampleHeightWidth pixelsH pixelsW
--resampleWidth pixelsW
--resampleHeight pixelsH
-Z, --resampleHeightWidthMax pixelsWH
-i, --addIcon
--optimizeColorForSharing
-o, --out file-or-directory
-j, --js file
Other functions:
--debug Enable debugging output
-h, --help Show help
-H, --helpProperties Show help for properties
--man Generate man pages
-v, --version Show the version
--formats Show the read/write formats
Git 相关
#!/usr/bin/env bash
# 获取当前tag 和 最新tag
current_tag=$(git describe --abbrev=0)
latest_tag=$(git describe --tags $(git rev-list --tags --max-count=1))