常用bash脚本
1. 简介
本文介绍了一些常用事务处理的脚本,我这些脚本被放置在~/tools/bash
目录,并在~/tools/tools.sh
中定义了相关的alias
,tools.sh
文件在~/.bashrc
中加载进来。其中有些命令在git-bash
和Linux
下都适用的。
2. ~/.bashrc
通常在~/.bashrc
中定义一些环境变量和一些专用别名。
#!/bin/bashexport MYTOOLS=$HOME/tools
export MYBINS=$HOME/bin
export PATH=$MYBINS:$PATH
export LANG=zh_CN.UTF-8# Some alias defined in /etc/baseprofile
alias cdd='cd /c/Users/$USER/Desktop'test -f $MYBINS/bin.sh && . $MYBINS/bin.sh
test -f $MYTOOLS/tools.sh && . $MYTOOLS/tools.sh
3. $MYTOOLS/tools.sh
该脚本用于定义常用脚本的别名以方便调用。
#!/bin/bashalias cd=". $MYTOOLS/bash/cd.sh"
alias agg=". $MYTOOLS/bash/agg.sh"
alias cgg=". $MYTOOLS/bash/cgg.sh"
alias gg=". $MYTOOLS/bash/gg.sh"
alias kgg=". $MYTOOLS/bash/kgg.sh"
alias grw=". $MYTOOLS/bash/grw.sh"
alias gr=". $MYTOOLS/bash/gr.sh"
alias grv=". $MYTOOLS/bash/grv.sh"
alias del="$MYTOOLS/bash/del.sh"
alias ndel="$MYTOOLS/bash/ndel.sh"
alias pbin="$MYTOOLS/bash/pbin.sh"alias jd=". $MYTOOLS/bash/jd.sh"
alias aasm=". $MYTOOLS/bash/aasm.sh"
alias abin=". $MYTOOLS/bash/abin.sh"
alias ocd="$MYTOOLS/bash/ocd.sh"func_pushd() { \pushd $@ > /dev/null; }
alias pushd='func_pushd'
alias cdirs=". $MYTOOLS/bash/cdirs.sh"
alias dirs='dirs -v'
alias vi='vim'
alias ll='\ls -l --color=tty'
alias l.='\ls -d .* --color=tty'
alias cp='\cp -i'
alias rm='\rm -i'
alias mv='\mv -i'
4. 通用脚本
这些脚本被放置在$MYTOOLS/bash
目录下。
4.1 cd.sh
该脚本实现了将Windows格式路径修改为Linux格式路径,然后再使用cd
命令进入对应的目录。
#!/bin/bashWIN_PATH="$1"
LINUX_PATH=${WIN_PATH//\\//}
# git-bash
PREFIX=/
# MobaXterm
#PREFIX=/drives/
# wsl
#PREFIX=/mnt/
# VMware-Ubuntu
#PREFIX=/mnt/windows/if [[ $LINUX_PATH == *:* ]]; thenDISK_NAME=$(echo ${LINUX_PATH%:*} | tr 'A-Z' 'a-z')LINUX_PATH=${LINUX_PATH#*:}LINUX_PATH=$PREFIX${DISK_NAME}${LINUX_PATH/:/}
fiif [ -z "$LINUX_PATH" ]; then\cd
else\cd "$LINUX_PATH"
fi
unset DISK_NAME
unset LINUX_PATH
unset WIN_PATH
unset PREFIX
使用alias
重新定义cd
命令,使用的时候对于Linux格式的命令和之前的一样,对于Windows格式的命令需要将路径使用"
或'
包括起来。
# 定义别名,”.“表示在当前bash环境下执行该脚本,不要另外启动一个bash环境
$ alias cd=". ~/tools/bash/cd.sh"
# Linux格式的路径,不需要加引号
$ cd /D/apps_no_install/Git
$ cd //192.168.1.31/共享文件
# Windows格式的路径,需要加引号
$ cd 'D:\apps_no_install\Git_readme'
$ cd '\\192.168.1.31\共享文件'
4.2 bin_update.sh
该脚本用于将windows系统中安装的一些程序汇总到一个目录,避免PATH
中路径过多,该文件需要根据实际情况进行增删。$MYBIN
是我在~/.bashrc
中定义的环境变量,其值为~/bin
,EXE_PATH
应根据具体环境决定,比如c盘在git-bash
中对应/c/
、MobaXterm
中对应/drivers/c/
、wsl
中对应/mnt/c/
,另外在wsl
中盘符仅支持小写。
#!/bin/bashmkdir -p $MYBINSfunc_link()
{NUM=${#FILEOLD[@]}for ((i=0; i<NUM; i++)); doecho -e "#!/bin/bash\nPATH=\"$EXE_PATH\":$""PATH \"$EXE_PATH/$PREFIXOLD${FILEOLD[$i]}$SUFFIXOLD\" \"$""@\"" > "$MYBINS/$PREFIXNEW${FILENEW[$i]}$SUFFIXNEW"if [ $? -ne 0 ]; thenecho "$EXE_PATH/$PREFIXOLD${FILEOLD[$i]}$SUFFIXOLD"fichmod +x "$MYBINS/$PREFIXNEW${FILENEW[$i]}$SUFFIXNEW"done
}EXE_PATH=/d/Keil_v5_40/UV4
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(uv4)
FILENEW=(uv4)
#func_linkEXE_PATH=/d/Keil_v5_40/UV4
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".com"
SUFFIXNEW=""
FILEOLD=(uVision)
FILENEW=(uVision)
#func_linkEXE_PATH=/d/Keil_v5_40/ARM/ARMCLANG/bin
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(armclang clang-format armar armasm armlink fromelf)
FILENEW=(armclang clang-format armar armasm armlink fromelf)
#func_linkEXE_PATH=/d/apps_no_install/csky-elfabiv2-tools-mingw-minilibc-20210423/bin
PREFIXOLD=csky-elfabiv2-
PREFIXNEW=csky-abiv2-elf-
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(ar as g++ gcc gcov gdb ld nm objcopy objdump readelf size strip strings)
FILENEW=(ar as g++ gcc gcov gdb ld nm objcopy objdump readelf size strip strings)
#func_linkEXE_PATH=/d/apps_no_install/arm-gnu-toolchain-13.2.Rel1-mingw-w64-i686-arm-none-eabi/bin
PREFIXOLD=arm-none-eabi-
PREFIXNEW=arm-none-eabi-
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(ar as g++ gcc gcov gdb ld nm objcopy objdump readelf size strip strings)
FILENEW=(ar as g++ gcc gcov gdb ld nm objcopy objdump readelf size strip strings)
#func_linkEXE_PATH=/d/Qt/Qt5.11.2/Tools/mingw530_32/bin
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(ar as g++ gcc gcov gdb ld nm objcopy objdump readelf size strip strings windres)
FILENEW=(ar as g++ gcc gcov gdb ld nm objcopy objdump readelf size strip strings windres)
#func_linkEXE_PATH=/d/Qt/Qt5.11.2/5.11.2/mingw53_32/bin
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(windeployqt)
FILENEW=(windeployqt)
#func_linkEXE_PATH="/d/Program Files/CMake/bin"
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(cmake-gui cmake)
FILENEW=(cmake-gui cmake)
#func_linkEXE_PATH="/d/apps_no_install/OpenOCD-20231002-0.12.0/bin"
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(openocd)
FILENEW=(openocd)
#func_linkEXE_PATH="/d/Program Files/Pandoc"
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(pandoc)
FILENEW=(pandoc)
#func_linkEXE_PATH="/mnt/d/apps_no_install/Beyond Compare"
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(BCompare)
FILENEW=(_BCompare)
func_linkEXE_PATH="/mnt/d/Program Files/Notepad++"
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(notepad++)
FILENEW=(_notepad++)
func_linkEXE_PATH="/c/Users/xflm/tools/gcc/bin"
PREFIXOLD=""
PREFIXNEW=""
SUFFIXOLD=".exe"
SUFFIXNEW=""
FILEOLD=(float2hex hex2float hex2u32)
FILENEW=(float2hex hex2float hex2u32)
#func_link
该命令不常用,故不需要定义alias,这里将PATH
新增了可执行文件的路径,因为可执行文件可能会调用其所在目录里的动态库。
$ ~/tools/bash/bin_update.sh
$ ls bin
ar arm-none-eabi-ar ...
$ cat ~/bin/ar
#!/bin/bash
PATH="/d/Qt/Qt5.11.2/Tools/mingw530_32/bin":/c/Users/xflm/tools/gcc/bin:/c/Users/xflm/bin:/mingw64/bin:/usr/bin:/c/Users/xflm/bin:/d/project/apps_no_install/Python37/Scripts:/d/project/apps_no_install/Python37:/c/Windows/system32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0 "/d/Qt/Qt5.11.2/Tools/mingw530_32/bin/ar.exe" "$@"
4.3 agg.sh
该脚本实现了对arm-none-eabi-gdb
的调用,主要是开启了gdb的命令记录功能,也即退出gdb后,gdb会将历史命令写入到文件中,等下次启动gdb后自动读取进来。$MYTOOLS
是我定义在~/.bashrc
的一个环境变量,其值为~/tools
。common.gdb
是个gdb脚本文件,下文会展示。
#!/bin/bashif [ -f .gdb_history ]; thentmp_GDB_HISTORY=.gdb_history
elsetmp_GDB_HISTORY=$MYHOME/.gdb_history
fiarm-none-eabi-gdb \
-q "$@" \
-ex "set history filename $tmp_GDB_HISTORY" \
-x $MYTOOLS/gdb/arm/init.gdbunset tmp_GDB_HISTORY
init.gdb的内容如下。
# 关闭退出再确认功能
set confirm off
# 关闭分页显示功能
set pagination off
# 开启历史记录保存功能
set history save on
# 开启打印格式化
set print pretty
使用alias
定义agg
命令,以后就可以使用agg
替代arm-none-eabi-gdb
。
$ alias agg=". ~/tools/bash/agg.sh"
4.4 cgg.sh
该脚本功能和agg.sh
相似。
#!/bin/bashif [ -f .gdb_history ]; thentmp_GDB_HISTORY=.gdb_history
elsetmp_GDB_HISTORY=$MYHOME/.gdb_history
ficsky-abiv2-elf-gdb \
-q "$@" \
-ex "set history filename $tmp_GDB_HISTORY" \
-x $MYTOOLS/gdb/csky/init.gdbunset tmp_GDB_HISTORY
4.5 gg.sh
该脚本功能和agg.sh
相似。
#!/bin/bashif [ -f .gdb_history ]; thentmp_GDB_HISTORY=.gdb_history
elsetmp_GDB_HISTORY=$MYHOME/.gdb_history
figdb \
-q "$@" \
-ex "set history filename $tmp_GDB_HISTORY" \
-x $MYTOOLS/gdb/host/init.gdbunset tmp_GDB_HISTORY
4.6 kgg.sh
有时候gdb
运行卡住了,或则我们想退出gdb
但是不想打断cpu的运行,则我们可以通过kill命令结束gdb
进程,搜索中限定了$USER
故不会介素别人开启的gdb
进程。
#!/bin/bashtmp_GDBPID=$(ps -ef | grep -w gdb | grep $USER | grep -v grep | awk '{print $2}')if [ -z "$tmp_GDBPID" ]; thenecho "kgg: No gdb process is running"return 1
fikill -9 $tmp_GDBPIDunset tmp_GDBPID
4.7 grw.sh
该脚本主要是用于搜索当前目录及子目录中.c``.h``.s``.S
文件中的word
,比如搜索函数名或变量名啥的。
#!/bin/bashif [ $# -ne 1 ]; thenecho "grw [content]"return 1
fiecho -e "\033[35m====================\033[0m"
\grep $1 -nrw --color=auto --include=*\.[chsS] --include=[Mm]akefile
echo -e "\033[35m====================\033[0m"
使用alias
定义grw
命令。
$ alias grw=". ~/tools/bash/grw.sh"
# 如在某工程目录下使用,搜索到的内容会有颜色显示的
$ grw main
====================
gpio_ex2_toggle/src/gpio_ex2_toggle.c:28:int main(void)
====================
4.8 gr.sh
该脚本和grw.sh
类似,只是grep
的参数中少了w
,也即允许匹配word
内部的内容。
#!/bin/bashif [ $# -ne 1 ]; thenecho "gr [content]"return 1
fiecho -e "\033[35m====================\033[0m"
\grep $1 -nr --color=auto --include=*\.[chsS] --include=[Mm]akefile
echo -e "\033[35m====================\033[0m"
4.9 grv.sh
该脚本用于快速打开grep
搜索的内容,grep
搜索的结果中包含了行号,vim
可以直接定位到该行号。
#!/bin/bashif [ $# -eq 0 ]; thenecho "grv [content]"return 1
elsefor i in $*; dotmp_FILE+=$(echo $i | awk -F ':' '{print $1" +"$2" "}')donevim $tmp_FILE -On
fiunset tmp_FILE
使用alias
定义grv
命令。
$ alias grv=". ~/tools/bash/grv.sh"
# 如在某工程目录下将grw搜索的内容,通过grv查看,通常下面这一串,在git-bash中双击就选中了,选中后右键一次表示复制,再右键一次表示粘贴,十分方便
$ grv gpio_ex2_toggle/src/gpio_ex2_toggle.c:28:int
4.10 del.sh
该脚本用于模拟一个简单的文件回收站,使用该脚本进行文件删除时,该脚本会将文件剪切到~/null
目录,未来可通过ndel.sh
进行恢复,文件/目录等都可以被回收。
#!/bin/bashRECYCLE_DIR=~/null
RECYCLE_LOG=.logif [ $# -eq 0 ]; thenif [ ! -d $RECYCLE_DIR ]; thenecho "You have deleted nothing."exit 0fiLAST_DIR=$(\ls $RECYCLE_DIR | tail -n 1)if [ -z "$LAST_DIR" ]; thenecho "You have deleted nothing."rm -rf $RECYCLE_DIRexit 0ficat $RECYCLE_DIR/$LAST_DIR/$RECYCLE_LOGexit 0
fiNEW_DIR=$RECYCLE_DIR"/"$(date +%F)
NEW_LOG=$NEW_DIR/$RECYCLE_LOG
[ -d $NEW_DIR ] || mkdir -p $NEW_DIR
NEW_TIME=$(date +%H%m%M%S)
LOG=""for DEL_FILE in $@; doREC_FILE=$NEW_DIR/$NEW_TIME"."$(basename $DEL_FILE)mv $DEL_FILE $REC_FILEif [ $? -eq 0 ]; thenif [[ "$DEL_FILE" == /* ]]; thenecho -e "$REC_FILE $DEL_FILE" >> $NEW_LOGelseecho -e "$REC_FILE $PWD/$DEL_FILE" >> $NEW_LOGfielseexit 0fi
done
使用时通过alias
定义一个del
命令。
# 定义别名,脚本中变量比较多,不在当前bash环境执行
$ alias del=~/tools/bash/del.sh
# 新建目录,并创建个测试文件
$ mkdir aa && touch aa/1.txt
# 删除文件
$ del aa/1.txt
# 查看删除日志
$ del
/c/Users/xflm/null/2024-01-17/11015156.1.txt /d/apps_no_install/aa/1.txt
# 查看回收站的内容
$ ls ~/null/*/*
/c/Users/xflm/null/2024-01-17/11015156.1.txt
4.11 ndel.sh
该脚本用于恢复del
删除的文件。
#!/bin/bashRECYCLE_DIR=~/null
RECYCLE_LOG=.logif [ $# -eq 0 ]; thenecho "Usage: ndel [deleted file below]"ls $RECYCLE_DIR/*/*exit 0
fifor FILE in $@; doif [ -f $FILE ]; thenFILE_DIR=$(dirname $FILE)FILE_NAME=$(basename $FILE)elseFILE_NAME=${FILE##*/}if [ -z "$FILE_NAME" ]; thenLOG1=${FILE%/*}FILE_NAME=${LOG1##*/}FILE_DIR=${LOG1%/*}elseFILE_DIR=${FILE%/*}fifiLOG1=$(grep -n "/$FILE_NAME\b" $FILE_DIR/$RECYCLE_LOG)if [ -z "$LOG1" ]; thenecho "No this file delete log."exit 1fiLINE=${LOG1%%:*}LOG2=${LOG1#*:}RECYCLE_FILE=${LOG2% *}ORIGINAL_FILE=${LOG2#* }if [ -e "$ORIGINAL_FILE" ]; thenecho "$ORIGINAL_FILE: The file is exist, cannot recovery."exit 1fised -i "$LINE"d $FILE_DIR/$RECYCLE_LOGif [ ! -e $FILE ]; thenecho $FILE: Not exist.exit 1fimv $LOG2if [ ! -s $FILE_DIR/$RECYCLE_LOG ]; thenrm -f $FILE_DIR/$RECYCLE_LOGif [ -z "$(\ls $FILE_DIR)" ]; thenrm -rf $FILE_DIRelseecho $FILE_DIR: Dir has files, but log is empty.fifi
done
使用时通过alias
定义一个ndel
命令。
# 定义别名,脚本中变量比较多,不在当前bash环境执行
$ alias ndel=~/tools/bash/ndel.sh
# 查看删除日志
$ ndel
Usage: ndel [deleted file below]
/c/Users/xflm/null/2024-01-17/11015156.1.txt
# 恢复被删除的内容
$ ndel /c/Users/xflm/null/2024-01-17/11015156.1.txt
4.12 pbin.sh
封装了一下od
命令用于查看bin文件内容。
#!/bin/bashfunc_help()
{cat << EOF
Usage: pbin [options] file-h Print this usage-f FileName-s StartAddress Default 0x0-p PrintAddress Default StartAddress-t PrintType Default x4a named character, ignoring high-order bitc printable character or backslash escaped[SIZE] signed decimal, SIZE bytes per integerf[SIZE] floating point, SIZE bytes per integero[SIZE] octal, SIZE bytes per integeru[SIZE] unsigned decimal, SIZE bytes per integerx[SIZE] hexadecimal, SIZE bytes per integer-w BytesPerLine Defult 16-n PrintBytes Default 16
EOF
}START_ADDR=0x0
PRINT_ADDR=""
PRINT_TYPE=x4
LINE_BYTES=16
PRINT_BYTES=16while getopts "hf:s:p:t:w:n:" optname; docase "$optname" in"h")func_helpexit;;"f")BIN_FILE=$OPTARG;;"s")START_ADDR=$OPTARG;;"p")PRINT_ADDR=$OPTARG;;"t")PRINT_TYPE=$OPTARG;;"w")LINE_BYTES=$OPTARG;;"n")PRINT_BYTES=$OPTARG;;":")echo No argument value for option $OPTARGexit -1;;"?")echo Unknow option $OPTARGexit -1;;esac
doneif [ -z "$BIN_FILE" ]; thenecho Error: no bin file. Use -h for help.exit -1
fiif [ -z "$PRINT_ADDR" ]; thenPRINT_ADDR=$START_ADDR
fi# Must be converted to hexadecimal, otherwise will be set to base 8.
PRINT_ADDR=$(printf "0x%08x" $PRINT_ADDR)
START_ADDR=$(printf "0x%08x" $START_ADDR)
SKIP_ADDR=$(printf "0x%08x" $((PRINT_ADDR-START_ADDR)))od -t $PRINT_TYPE -Ax -v -w$LINE_BYTES --traditional -N$PRINT_BYTES $BIN_FILE +$SKIP_ADDR +$PRINT_ADDR
使用示例。
# 默认查看bin文件的前4个words
$ pbin -f test.bin
000000 (000000) 6f6e6e49 74655320 55207075 736e696e
000010 (000010)
# 将test.bin映射到0x08000000,查看0x08000010开始的24个字节的数据,使用16进制显示,每行显示8个字节
$ pbin -f test.bin -s 0x08000000 -p 0x08000010 -n 24 -t x1 -w 8
000010 (8000010) 74 61 6c 6c 20 4c 6f 67
000018 (8000018) 20 28 62 29 20 36 34 2d
000020 (8000020) 62 69 74 00 00 00 00 00
000028 (8000028)
4.13 cdirs.sh
Shell中有pushd
和popd
两个命令,前者将当前工作路径压入队列,后者从队列中取出一个路径,并跳转过去。还有个dirs
命令,用于显示这个队列中的内容,此处基于dirs
做了一个路径选择的交互式脚本,可用于在多个路径之间跳转,该脚本还提供了历史路径的保存与恢复功能。
#!/bin/bash\dirs -v
echo "<s>:Save; <l>:Load; <c>:Clear; <d>:Delete"
read -p "cd: " _PATH_NUMif [[ ! -z "$_PATH_NUM" ]]; thencase $_PATH_NUM in"");;s)echo "Save history to file"\dirs -p | sort -u | sed 's/^/\\pushd -n /g' > ~/.cdirs_history;;l)echo "Load form file"[[ -e ~/.cdirs_history ]] && . ~/.cdirs_history > /dev/null;;c)echo "Clear all entry"\dirs -c;;d*)_PATH_NUM=$(echo $_PATH_NUM | sed 's/d\s*//g')echo "Delete "$_PATH_NUM\popd +$_PATH_NUM > /dev/null;;*) \pushd +$_PATH_NUM > /dev/null;;esac
fiunset _PATH_NUM
5. 专用脚本
5.1 jd.sh
使用JLinkGdbServerCL命令行程序开启gdbserver。
#!/bin/bashtmp_JLinkGDBServer="D:/Program Files (x86)/SEGGER/JLink_V622/JLinkGDBServerCL.exe""$tmp_JLinkGDBServer" \
-silent \
-nologtofile \
-device Cortex-M3 \
-if SWD \
-speed auto \
-noir \
-noLocalhostOnly \
-select usb \
-endian little \
-port 1027 \
-SWOPort 2332 \
-noreset \
-nohalt \
"$@"# User can use cmd below to change the script settings:
# -nosilent
# -log [file]
# -port [port]
# -SWOPort [port]
# -select IP=[IP:Port]unset tmp_JLinkGDBServer
5.2 aasm.sh
使用Keil工具链中的fromelf
程序生成反汇编文件。
#!/bin/bashfunc_help()
{echo Uasge: aasm *.axf *.asm
}if [ $# -ne 2 ]; thenfunc_help
elsefromelf -a -c --text --output $2 $1
fi
5.3 abin.sh
使用Keil工具链中的fromelf
程序生成bin文件。
#!/bin/bashfunc_help()
{echo Uasge: abin *.axf *.bin
}if [ $# -ne 2 ]; thenfunc_help
elsefromelf --bin --output $2 $1
fi
5.4 ocd.sh
使用openocd
工具创建连接。
#!/bin/bashcd /d/apps_no_install/OpenOCD-20231002-0.12.0/bin
./openocd -f interface/stlink-dap.cfg -f target/stm32h7x.cfg```