天天看点

Git需求: 快速切换项目目录

不知道你有没有和我一样的需求,平常会负责几个项目的开发,又习惯使用git命令行管理开发代码。每次要提交代码,都要找到项目的目录,然后打开一个命令行窗口,或者频繁切换目录到想要的目录,到最后,你的任务栏可能有N个git命令行窗口,虽然操作也没那么麻烦,但是做重复的事总是很不舒服。

所以简单的写了个bash脚本,方便切换目录,虽然简单,但是用着还觉得挺方便的,有兴趣的可以看下,https://github.com/eussi/SimpleScripts/tree/master/gitTools。

我用的电脑是windows系统,在我电脑上开发环境是这样的,自己有个工作目录,负责的项目都放在这个目录下面,每个项目有他自己的标识。假设这个目录是APP,下面A,B,C等项目,目录层级可能如下:

./APP
./APP/aaaa
./APP/aaaa/aaaa
./APP/aaaa/aaaa/app.properties
./APP/bbbbb
./APP/bbbbb/app.properties
./APP/cccccccc
./APP/cccccccc/app.properties
./APP/dddd
           

标识每个项目信息的文件是app.properties,文件里内容可能包含appid等应用的数据,文件内容可能是这样:

appid=100012345
bb=e
cc=3
           

当然文件app.properties可能不存在,文件中的appid也可能不存在。

#1

刚刚开始想法是这样的,需要有一个命令,这个命令不能很长,传入工作目录,这个命令会将所有项目以列表的形式展示出来,然后自己输入想进入哪个项目目录,就可以立刻进入,并且这个命令可以在电脑的任何一个目录下执行。

这样我随时打开git,随时切换目录,即使在一个项目的目录中,也可以切换到另外一个项目的目录下。

‘很短的命令’可以通过alias别名来实现,逻辑可以通过bash脚本来实现,切换当前所在目录,可以通过

source或.

执行bash脚本来实现。

于是有了下面一个版本的脚本

list_v1.sh

#!/bin/bash
#program:
#  Switch application directory
#author:
#  xuemingwang 2020-11-21
#usage:
#  source list_v1.sh dir  

#print format
WHITE="\033[37m"
RED="\033[31m"
BOLD="\033[1m"

#print func
printMsg() {
    mesg=$1
    head=$2
    tail=$3
    echo -e "${head}${mesg}${tail}"
}


ROOT_DIR=$1
APP_FILENAME=app.properties
APPID_SYMPOL=appid
NULL_APPID=#########


#main
main() {
    echo ""
    echo "******************************"
    printMsg "APP_DIR:"$ROOT_DIR $WHITE $BOLD
    echo "******************************"
    #get list
    echo -e "\nLIST:"
    index=0
    for var in `ls $ROOT_DIR`
    do
	subDir=$ROOT_DIR"/"$var
	if [ ! -d $subDir ]; then
            continue
		
	fi
	#find appid
	appfilePath=`find $subDir -name $APP_FILENAME`

        if [ -n "$appfilePath" ]; then
	    app_arr[index]="["$index"]. "`sed -n '/^'"$APPID_SYMPOL"'=\([0-9]*\)/s//\1/p' $appfilePath`" "$var
        else
	    app_arr[index]="["$index"]. "$NULL_APPID" "$var
	fi
	#echo "${app_arr[index]}"
	index=$((index+1))
    done
    #print list
    for ((k=0; k<$index; k++))
    do
        printMsg "${app_arr[$k]}" $WHITE $BOLD
    done
    #choose where to to
    echo "******************************"
    echo -e "\nOPTIONS:"
    while true
    do
	echo Please choose where to go or exit by Q:
        read input
	#echo $input
	if [ "Q" = "$input" -o "q" = "$input" ]; then
	    echo "exit"
	    #exit 0
	    break
	fi
	find=0
	for ((k=0; k<$index; k++))
	do
            echo "${app_arr[$k]}" | grep "$input" > /dev/null 2>&1
	    if [ $? -eq 0 ]; then
                #find
		find=1
		retDir=`echo ${app_arr[$k]} | awk '{print $3}'`
                cd $ROOT_DIR"/"$retDir 
		break
	    fi
        done
	#for remove exit
	if [ "$find" = "1" ]; then
	    break
	fi
	echo -e "Please enter the string contained in the list.\n"
    done
}


if [ ! $# -eq 1 ]; then
    echo Usage: with one parameters, the root of application directory.
    #exit -1 #Exit cannot be used in order to switch execution directories
else
    if [ ! -d $ROOT_DIR ]; then
        echo $ROOT_DIR "is not exist."
        #exit -1  #Exit cannot be used in order to switch execution directories
    else
        if [ ! -d $ROOT_DIR ]; then
	    echo $ROOT_DIR "is not exist."
	else
	    main
	fi
    fi
fi
           

大致逻辑是遍历工作目录,并通过find命令查找标识文件app.properties,再在文件中提取出appid对应的值,通过列表的形式展示出来。

此时将脚本加入path路径中,我是在home目录里创建了bin目录,即C:\Users\wangx\bin,脚本放在该目录下,然后打开bash命令行通过alias设置命令别名,如下:

$ alias cmds='. list_v1.sh /C/Users/wangx/Desktop/APP'
           

上面命令用

cmds

(cmd switch)代替

. list_v1.sh /C/Users/wangx/Desktop/APP

命令,注意运行该命令的时候我使用的

.

或者

source

, 因为要切换当前执行的目录,使用

sh

只能切换子shell的目录,对当前目录切换无效。

这个时候便可以方便切换目录了,操作如下:

$ cmds

******************************
APP_DIR:/C/Users/wangx/Desktop/APP
******************************

LIST:
[0]. 100012345 aaaa/
[1]. 100016789 bbbbb/
[2]. 200016789 cccccccc/
[3]. ######### dddd/
******************************

OPTIONS:
Please choose where to go or exit by Q:
16789

$ pwd
/C/Users/wangx/Desktop/APP/bbbbb

$ cmds

******************************
APP_DIR:/C/Users/wangx/Desktop/APP
******************************

LIST:
[0]. 100012345 aaaa/
[1]. 100016789 bbbbb/
[2]. 200016789 cccccccc/
[3]. ######### dddd/
******************************

OPTIONS:
Please choose where to go or exit by Q:
ddd

$ pwd
/C/Users/wangx/Desktop/APP/dddd

$ cmds

******************************
APP_DIR:/C/Users/wangx/Desktop/APP
******************************

LIST:
[0]. 100012345 aaaa/
[1]. 100016789 bbbbb/
[2]. 200016789 cccccccc/
[3]. ######### dddd/
******************************

OPTIONS:
Please choose where to go or exit by Q:
q
exit

           

这样看起来已经完全满足需求了,于是拿到自己电脑上开始使用了,一用就尴尬了,工作目录下一二十个应用,每次要切换目录时,输入命令,展示出来实在是太慢了,还不如我之前直接手动切换。

#2

一方面git的bash命令行执行linux上的一些命令速度确实慢很多,另一方面怀疑自己使用find命令查找appid效率可能太低了,毕竟一个项目中文件可能很多,查找文件数量多起来将会耗时很高,两方面一起算下来,脚本执行速度慢也是理所当然的吧。于是想着先优化下find命令吧。

一般在一个公司,所有的项目的appid存放的位置一般是一样的,比如都在应用根目录下有个app.properties,或者在第二层目录下,所以就没必要通过find搜索一遍了,采用直接读取这两个位置的app.properties,拿到appid,拿不到就结束。即:

getAppId() {
    rootDir=$1
    filePath=$ROOT_DIR'/'$rootDir'/'$APP_FILENAME
    #src\main\resources\META-INF\app.properties
    if [ -f "$filePath" ]; then
        echo `sed -n '/^'"$APPID_SYMPOL"'=\([0-9]*\)/s//\1/p' $filePath`
    else
        find=0
        for var in `ls $ROOT_DIR'/'$rootDir`
        do
            secondFilePath=$ROOT_DIR'/'$rootDir'/'$var'/'$APP_FILENAME
	    #echo $secondFilePath
	    if [ -f "$secondFilePath" ]; then
                echo `sed -n '/^'"$APPID_SYMPOL"'[ ]*=[ ]*\([0-9]*\)/s//\1/p' $secondFilePath`
                find=1
		break  #At most two layer
            fi
	done
	if [ $find = "0" ]; then
            echo $NULL_APPID
        fi
    fi
}
           

于是有了第二版脚本

list_v2.sh

:

#!/bin/bash
#program:
#  Switch application directory
#author:
#  xuemingwang 2020-11-21
#usage:
#  source list.sh dir  

#print format
WHITE="\033[37m"
RED="\033[31m"
BOLD="\033[1m"

#print func
printMsg() {
    mesg=$1
    head=$2
    tail=$3
    echo -e "${head}${mesg}${tail}"
}


ROOT_DIR=$1
APP_FILENAME=app.properties
APPID_SYMPOL=appid
NULL_APPID=#########

getAppId() {
    rootDir=$1
    filePath=$ROOT_DIR'/'$rootDir'/'$APP_FILENAME
    #src\main\resources\META-INF\app.properties
    if [ -f "$filePath" ]; then
        echo `sed -n '/^'"$APPID_SYMPOL"'=\([0-9]*\)/s//\1/p' $filePath`
    else
        find=0
        for var in `ls $ROOT_DIR'/'$rootDir`
        do
            secondFilePath=$ROOT_DIR'/'$rootDir'/'$var'/'$APP_FILENAME
	    #echo $secondFilePath
	    if [ -f "$secondFilePath" ]; then
                echo `sed -n '/^'"$APPID_SYMPOL"'[ ]*=[ ]*\([0-9]*\)/s//\1/p' $secondFilePath`
                find=1
		break  #At most two layer
            fi
	done
	if [ $find = "0" ]; then
            echo $NULL_APPID
        fi
    fi
}


#main
main() {
    echo ""
    echo "******************************"
    printMsg "APP_DIR:$ROOT_DIR" $WHITE $BOLD
    echo "******************************"
    #get list
    echo -e "\nLIST:"
    index=0
    for var in `ls $ROOT_DIR`
    do
	subDir=$ROOT_DIR"/"$var
	if [ ! -d $subDir ]; then
            continue
		
	fi
	#find appid
	#appfilePath=`find $subDir \( -path "./.git" -o -path ".git" -o -path "./.idea" \) -prune -o -name $APP_FILENAME`
	appId=`getAppId $var`

        #if [ -n "$appfilePath" ]; then
	#    app_arr[index]="["$index"]. "`sed -n '/^'"$APPID_SYMPOL"'=\([0-9]*\)/s//\1/p' $appfilePath`" "$var
        #else
	#    app_arr[index]="["$index"]. "$NULL_APPID" "$var
	#fi

	app_arr[index]=`printf "[%02s]. %-9s %s" "$index" "$appId" "$var"`
        printMsg "${app_arr[index]}" $WHITE $BOLD
	
	#echo "${app_arr[index]}"
	index=$((index+1))
    done
    #print list
    #for ((k=0; k<$index; k++))
    #do
    #    printMsg "${app_arr[$k]}" $WHITE $BOLD
    #done
    #choose where to to
    echo "******************************"
    echo -e "\nOPTIONS:"
    while true
    do
	echo Please choose where to go or exit by Q:
        read input
	#echo $input
	if [ "Q" = "$input" -o "q" = "$input" ]; then
	    echo "exit"
	    #exit 0
	    break
	fi
	find=0
	for ((k=0; k<$index; k++))
	do
            echo "${app_arr[$k]}" | grep "$input" > /dev/null 2>&1
	    if [ $? -eq 0 ]; then
                #find
		find=1
		retDir=`echo ${app_arr[$k]} | awk '{print $3}'`
                cd $ROOT_DIR"/"$retDir
		echo -e "\nCURRENT: "$ROOT_DIR"/"$retDir
		break
	    fi
        done
	#for remove exit
	if [ "$find" = "1" ]; then
	    break
	fi
	echo -e "Please enter the string contained in the list.\n"
    done
}


if [ ! $# -eq 1 ]; then
    echo Usage: with one parameters, the root of application directory.
    #exit -1 #Exit cannot be used in order to switch execution directories
else
    if [ ! -d $ROOT_DIR ]; then
        echo $ROOT_DIR "is not exist."
        #exit -1  #Exit cannot be used in order to switch execution directories
    else
        if [ ! -d $ROOT_DIR ]; then
	    echo $ROOT_DIR "is not exist."
	else
	    main
	fi
    fi
fi
           

同样将脚本设置放到path目录下,然后设置别名

$ alias cmds='. list_v2.sh /C/Users/wangx/Desktop/APP'
           

运行效果如下:

$ cmds

******************************
APP_DIR:/C/Users/wangx/Desktop/APP
******************************

LIST:
[00]. 100012345 aaaa/
[01]. 100016789 bbbbb/
[02]. 200016789 cccccccc/
[03]. ######### dddd/
******************************

OPTIONS:
Please choose where to go or exit by Q:
bbb

CURRENT: /C/Users/wangx/Desktop/APP/bbbbb/

$ pwd
/C/Users/wangx/Desktop/APP/bbbbb

$ cmds

******************************
APP_DIR:/C/Users/wangx/Desktop/APP
******************************

LIST:
[00]. 100012345 aaaa/
[01]. 100016789 bbbbb/
[02]. 200016789 cccccccc/
[03]. ######### dddd/
******************************

OPTIONS:
Please choose where to go or exit by Q:
q
exit

$ pwd
/C/Users/wangx/Desktop/APP/bbbbb
           

运行没问题,拿到自己电脑上测试,同样的问题,还是运行的太慢,看来命令不是主要的影响因素,之后也尝试网上查找优化git执行命令的速度,但是效果都不大。上面的方案看来并不能解决问题。

#3

因为git执行脚本始终速度都很慢,只能通过其他方法来实现自己的需求了。

再细想想,由于自己负责开发的项目数量和名称变动并不是很大,我可以将信息先缓存起来,等到数据变动时,我再进行更新,并且我也没必要每次列出来之后选择要进入的目录,我还想要一个立刻进入某目录的功能,切换速度更快更方便。

于是想了另外一个方案,修改脚本,传入不同的参数,实现三个功能:

  • 将项目数据缓存到某个文件中
  • 读取缓存文件,展示数据,选择进入目录
  • 读取缓存文件,直接进入目录

这样,直接读取一个缓存文件,然后选择进入某个项目的目录,不至于执行也是龟速吧。于是产生了下面的脚本

list_v3.sh

:

#!/bin/bash
#program:
#  Switch application directory
#author:
#  xuemingwang 2020-11-21
#usage:
#  source list_v3.sh dir -g
#  source list_v3.sh dir -l
#  source list_v3.sh dir -s str

#print format
WHITE="\033[37m"
RED="\033[31m"
BOLD="\033[1m"

#params
[email protected]
INPUT_PARAMS_NUM=$#

#variable
TEMP_FILE=/.list_v3
ROOT_DIR=$1
FLAG=$2
FLAG_VALUE=$3
APP_FILENAME=app.properties
APPID_SYMPOL=appid
NULL_APPID=#########

#print func
printMsg() {
    mesg=$1
    head=$2
    tail=$3
    echo -e "${head}${mesg}${tail}"
}

getAppId() {
    rootDir=$1
    filePath=$ROOT_DIR'/'$rootDir'/'$APP_FILENAME
    #src\main\resources\META-INF\app.properties
    if [ -f "$filePath" ]; then
        echo `sed -n '/^'"$APPID_SYMPOL"'=\([0-9]*\)/s//\1/p' $filePath`
    else
        find=0
        for var in `ls $ROOT_DIR'/'$rootDir`
        do
            secondFilePath=$ROOT_DIR'/'$rootDir'/'$var'/'$APP_FILENAME
	    #echo $secondFilePath
	    if [ -f "$secondFilePath" ]; then
                echo `sed -n '/^'"$APPID_SYMPOL"'[ ]*=[ ]*\([0-9]*\)/s//\1/p' $secondFilePath`
                find=1
		break  #At most two layer
            fi
	done
	if [ $find = "0" ]; then
            echo $NULL_APPID
        fi
    fi
}

#Generate temporary cache
generate() {
    tempFile=$TEMP$TEMP_FILE
	> $tempFile
	index=0
    for var in `ls $ROOT_DIR`
    do
	    subDir=$ROOT_DIR"/"$var
	    if [ ! -d $subDir ]; then
            continue
	    fi
	    appId=`getAppId $var`
	    printf "%-9s %s\n" "$appId" "$var" >> $tempFile
	    index=$((index+1))
    done

}

#List the options
list() {
    tempFile=$TEMP$TEMP_FILE
	if [ ! -f $tempFile ]; then
	    echo "err: need a temporary document."
	else
	    echo -e "\nLIST:"
	    index=0
        while read line
	    do
		    app_arr[index]=`printf "%02s %s" "$index" "$line"`
		    echo ${app_arr[index]}
		    index=$((index+1))
	    done < $tempFile
		echo -e "\nOPTIONS:"
		while true
        do
	        echo Please choose where to go or exit by Q:
            read input
	        #echo $input
	        if [ "Q" = "$input" -o "q" = "$input" ]; then
	            echo "exit"
	            #exit 0
	            break
	        fi
	        find=0
	        for ((k=0; k<$index; k++))
	        do
                echo "${app_arr[$k]}" | grep "$input" > /dev/null 2>&1
	            if [ $? -eq 0 ]; then
                    #find
		            find=1
		            retDir=`echo ${app_arr[$k]} | awk '{print $3}'`
                    cd $ROOT_DIR"/"$retDir
		            echo -e "\nCURRENT: "$ROOT_DIR"/"$retDir
		            break
	            fi
            done
	        #for remove exit
	        if [ "$find" = "1" ]; then
	            break
	        fi
	        echo -e "Please enter the string contained in the list.\n"
        done
	fi
}

#Switch directory
switch(){
    tempFile=$TEMP$TEMP_FILE
	if [ ! -f $tempFile ]; then
	    echo "err: need a temporary document."
	else
	    find=0
        while read line
	    do
		    echo "$line" | grep "$FLAG_VALUE" > /dev/null 2>&1
			if [ $? -eq 0 ]; then
				#find
				find=1
				retDir=`echo ${line} | awk '{print $2}'`
				cd $ROOT_DIR"/"$retDir
				echo -e "\nCURRENT: "$ROOT_DIR"/"$retDir
				break
			fi
	    done < $tempFile
		if [ "$find" != "1" ]; then
	        echo -e "No such app dir.\n"
	    fi
	    
	fi
}


#main
main() {
	if [[ $FLAG != -* ]]; then
		FLAG_VALUE=$FLAG
		FLAG="-s" #default value -s
	fi
	
	case $FLAG in
	    "-l")
		    list
			;;
		"-g")
		    generate
			;;
		"-s")
		    switch
			;;
		*)
		    echo "Unsupported commands."
		;;
	esac
}

#basic check
if [ $# -gt 3 ]; then
    echo Usage: Up to three parameters, root_dir, flag and the value.
    #exit -1 #Exit cannot be used in order to switch execution directories
else
    if [ ! -d $ROOT_DIR ]; then
        echo $ROOT_DIR "is not exist."
        #exit -1  #Exit cannot be used in order to switch execution directories
    else
        if [ ! -d $ROOT_DIR ]; then
	        echo $ROOT_DIR "is not exist."
	    else
	        main
	    fi
    fi
fi
           

同样将脚本设置放到path目录下,然后设置三个功能的别名

$ alias cmdg='sh list_v3.sh /C/Users/wangx/Desktop/APP -g'
$ alias cmdl='. list_v3.sh /C/Users/wangx/Desktop/APP -l'
$ alias cmds='. list_v3.sh /C/Users/wangx/Desktop/APP -s'

           

执行效果如下:

$ cmdg

$ cmdl

LIST:
00 100012345 aaaa/
01 100016789 bbbbb/
02 200016789 cccccccc/
03 ######### dddd/

OPTIONS:
Please choose where to go or exit by Q:
bb

CURRENT: /C/Users/wangx/Desktop/APP/bbbbb/

$ cmds aaa

CURRENT: /C/Users/wangx/Desktop/APP/aaaa/

$ cmds 2000

CURRENT: /C/Users/wangx/Desktop/APP/cccccccc/

$ pwd
/C/Users/wangx/Desktop/APP/cccccccc
           

其中cmdg生成文件内容如下:

$ cat $TEMP/.list_v3
100012345 aaaa/
100016789 bbbbb/
200016789 cccccccc/
######### dddd/
           

如果你往工作目录里添加了新的项目,运行一下

cmdg

更新下这个文件,然后又可以使用

cmdl

cmds

切换目录了,速度完全能达到需求。

终于算是搞定了。

使用了大概一个月了,确实用着比之前舒服多了。

另外,如果你有多个工作目录,这个脚本也可以使用,你可以将别名设置成不同的名字,来操作不同目录。不过需要注意的是,存放生成的文件要调整,否则会相互覆盖,可以将传入工作目录的字符串进行hash,然后拼接到生成文件名的后面来做区分。不过目前我这里这样就够了,就不多搞了。