【Shell开发】笔记&技巧

367 阅读2分钟

#【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

AppleScript.png

判断相关

判空

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

详见:www.cnblogs.com/ivyharding/…

图片处理

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))