目录
最近因为撸各种编译脚本, 碰到了不少脚本文件的问题, 而且很多东西你搜索都不知道该用什么关键词,
于是留个文备忘一下
参数相关
获取脚本文件所在目录
# sh
echo $(cd "$(dirname "$0")"; pwd)
rem bat
echo %~dp0
获取脚本文件自身
# sh
echo $0
rem bat
echo %0
获取所有参数
# sh
echo "$@"
# 注意, 传递时需要加引号, 否则带空格的参数会被拆为两个参数
sh other.sh "$@"
rem bat
echo %*
rem 注意, 传递时不能加引号, 否则会视为一个参数
other.bat %*
移动到下一个参数
# sh
echo $0
shift
echo $0
# 注意, shift 会影响 $@
echo $@
rem bat
echo %0
shift
echo %0
rem 注意, shift 不会影响 %*
echo %*
获取参数个数
# sh
echo $#
rem bat
set argC=0
for %%x in (%*) do set /A argC+=1
echo %argC%
除第一个参数外传递所有参数
# sh
# 从第二个开始
shift
sh other.sh "$@"
# 从第三个开始
shift 2
sh other.sh "$@"
# 注意: 必须使用 "$@", 不能先保存为变量再传递, 否则带空格的参数会被拆分为两个
# 注意: 以上方法会丢弃 shift 之前的参数, 使用前记得保存
# 上述是比较通用的实现方式, 各种 shell 都能用, 不介意兼容性的话, 也可以用这个
# 从第二个开始
${@:2}
# 从第三个开始
${@:3}
rem bat
# 注意, 必须要 enabledelayedexpansion, 否则 for/if 中的 !ALL_PARAMS! 变量无法正常展开
setlocal enabledelayedexpansion
rem 从第二个开始
set I=0
for %%x in (%*) do (
set /a I=I+1
if !I! geq 2 (set ALL_PARAMS=!ALL_PARAMS! %%x)
)
other.bat %ALL_PARAMS%
rem 从第三个开始
set I=0
for %%x in (%*) do (
set /a I=I+1
if !I! geq 3 (set ALL_PARAMS=!ALL_PARAMS! %%x)
)
other.bat %ALL_PARAMS%
变量判断相关
判断变量是否为空
# sh
if test "x$YourParam0" = "x"\
|| test "x$YourParam1" = "x"\
|| test "x$YourParam2" = "x"\
; then
echo show usage
exit 1
fi
rem bat
if not defined YourParam0 goto :usage
if not defined YourParam1 goto :usage
if not defined YourParam2 goto :usage
goto :run
:usage
echo show usage
exit /b 1
:run
变量值比较
# sh
if test "x-$A" = "x-xxx" ; then
echo "A == xxx"
fi
if ! test "x-$A" = "x-xxx" ; then
echo "A != xxx"
fi
if test $A -eq $B ; then
echo "A == B"
elif test $A -ne $B ; then
echo "A != B"
elif test $A -gt $B ; then
echo "A > B"
elif test $A -lt $B ; then
echo "A < B"
elif test $A -ge $B ; then
echo "A >= B"
elif test $A -le $B ; then
echo "A <= B"
fi
rem bat
if "%A%" == "xxx" (echo A == xxx)
if not "%A%" == "xxx" (echo A != xxx)
if %A% equ %B% (echo A == B)
if %A% neq %B% (echo A != B)
if %A% gtr %B% (echo A > B)
if %A% lss %B% (echo A < B)
if %A% geq %B% (echo A >= B)
if %A% leq %B% (echo A <= B)
变量值替换
# sh
MyVar=`echo $MyVar | sed -e "s/ReplaceFrom/ReplaceTo/g"`
rem bat
set MyVar=!MyVar:ReplaceFrom=ReplaceTo!
变量数值计算
# sh
RESULT=$((A+B))
rem bat
set /a RESULT=A+B
变量截取相关
截取父路径
# sh
SRC=a/b/c.txt
RESULT=${SRC%[/\\]*}
echo $RESULT
rem bat
set SRC=a/b/c.txt
for %%a in (%SRC%\..) do set RESULT=%%~fa
echo %RESULT%
截取文件名
# sh
SRC=a/b/c.txt
RESULT=${SRC##*[/\\]}
echo $RESULT
rem bat
set SRC=a/b/c.txt
for %%a in (%SRC%) do set RESULT=%%~nxa
echo %RESULT%
截取除扩展名外的文件名
# sh
SRC=a/b/c.txt
RESULT=$(basename ${SRC%.*})
echo $RESULT
rem bat
set SRC=a/b/c.txt
for %%a in (%SRC%) do set RESULT=%%~na
echo %RESULT%
截取扩展名
# sh
SRC=a/b/c.txt
RESULT=${SRC##*.}
echo $RESULT
rem bat
set SRC=a/b/c.txt
for %%a in (%SRC%) do set RESULT=%%~xa
set RESULT=%RESULT:~1,999%
echo %RESULT%
绝对路径转换为相对路径
# sh
RESULT=`realpath --relative-to="$REF_PATH" "$ABS_PATH"`
rem bat
@echo off
setlocal
set WORK_DIR=%~dp0
set ABS_PATH=%~2%
set REF_PATH=%~3%
if not defined ABS_PATH goto :usage
if not defined REF_PATH goto :usage
goto :run
:usage
echo usage:
echo path_abs2rel.bat RESULT ABS_PATH REF_PATH
exit /b 1
:run
for %%i in (%REF_PATH%) do set REF_PATH_TMP=%%~fi
for %%i in (%ABS_PATH%) do set ABS_PATH_TMP=%%~fi
if not exist "%REF_PATH_TMP%" (
RESULT=%ABS_PATH_TMP%
goto :return_result
)
if not exist "%ABS_PATH_TMP%" (
RESULT=%ABS_PATH_TMP%
goto :return_result
)
set SAME_PATH=%REF_PATH_TMP%
set SAME_PATH_MATCH=0
:same_path_loop
call set "MATCH_TMP=%%ABS_PATH_TMP:%SAME_PATH%=%%"
if not "%MATCH_TMP%" == "%ABS_PATH_TMP%" (
set SAME_PATH_MATCH=1
goto :same_path_loop_end
)
set SAME_PATH_OLD=%SAME_PATH%
for %%a in (%SAME_PATH%\..) do set SAME_PATH=%%~fa
if "%SAME_PATH%" == "%SAME_PATH_OLD%" goto :same_path_loop_end
goto :same_path_loop
:same_path_loop_end
if not %SAME_PATH_MATCH% == 1 (
RESULT=%ABS_PATH_TMP%
goto :return_result
)
set RESULT=
call set "REF_PATH_TMP=%%REF_PATH_TMP:%SAME_PATH%=%%"
:abs_parent_loop
if "x%REF_PATH_TMP%" == "x" (
set RESULT=.
goto :abs_parent_loop_end
)
set REF_PATH_TMP_OLD=%REF_PATH_TMP%
for %%a in (%REF_PATH_TMP%\..) do set REF_PATH_TMP=%%~fa
if "%REF_PATH_TMP%" == "%REF_PATH_TMP_OLD%" goto :abs_parent_loop_end
if "x%RESULT%" == "x" (
set RESULT=..
) else (
set RESULT=%RESULT%\..
)
goto :abs_parent_loop
:abs_parent_loop_end
call set "RESULT_TMP=%%ABS_PATH_TMP:%SAME_PATH%=%%"
set RESULT=%RESULT%%RESULT_TMP%
:return_result
endlocal & set "%1=%RESULT%"
相对路径转换为绝对路径
# sh
RESULT=$(cd -- "$RELATIVE_PATH" && pwd)
rem bat
for %%i in (%RELATIVE_PATH%) do set RESULT=%%~fi
返回值判断相关
函数调用和返回值
# sh
function add()
{
echo "$1 + $2 = $(($1+$2))"
return 0
}
RESULT=`add 2 3`
echo "run success: $?"
echo "run result: $RESULT"
rem bat
call :add RESULT 2 3
echo run success: %errorlevel%
echo run result: %RESULT%
goto :EOF
:add
setlocal
set /a _RESULT=%2+%3
endlocal & set "%1=%_RESULT%"
exit /b 0
退出当前脚本
N 为任意整数
# sh
exit N
rem bat
exit /b N
获取返回值和判断返回值
# sh
sh xxx.sh
result = "$?"
if test "$result" = "0"; then
echo success
fi
if test ! "$result" = "0"; then
echo failed
fi
rem bat
call xxx.bat
set result = %errorlevel%
if "%result%" == "0" (
echo success
)
if not "%result%" == "0" (
echo failed
)
rem 如果在 if 中使用 error level, 需要这样:
setlocal enabledelayedexpansion
if "1" == "1" (
call xxx.bat
if "!errorlevel!" == "0" (
echo success
)
)
执行外部命令并获取输出
# sh
result = `git config user.name`
rem bat
for /f "delims=" %%a in ('git config user.name') do @set result=%%a
rem 但是, 上述只能处理只有单行输出的命令, 对于多行输出命令, 必须用更奇葩的方式:
setlocal enabledelayedexpansion
rem 注意这边必须要有两个换行
(set NLM=^
)
set NL=^^^%NLM%%NLM%^%NLM%%NLM%
(
for /f "tokens=*" %%a in ('dir /b') do (
if not defined result (
set result=%%a
) else (
set result=!result!%NL%%%a
)
)
) >nul 2>&1
rem 注意 errorlevel 无法获取到命令的错误码
rem echo %errorlevel%
rem 注意这个感谈号, 以及 enabledelayedexpansion
echo !result!
管道相关
忽略输出和错误信息
# sh
sh xxx.sh >/dev/null 2>&1
rem bat
call xxx.bat >nul 2>&1
rem ignore error only
call xxx.bat 2>nul
输出和错误信息都输出到同一个文件
# sh
sh xxx.sh >filepath.log 2>&1
rem bat
call xxx.bat >filepath.log 2>&1
其它
判断文件或目录是否存在
# sh
if test -e "filepath"; then
echo exist
fi
rem bat
if exist "filepath" (
echo exist
)
删除空目录
# sh
find "$DST_PATH" -depth -type d -empty -exec rm -rf {} ';' >/dev/null 2>&1
rem bat
rem 这种方式在 cygwin 下无法使用, 因为 cygwin 里面也有 sort
for /f "delims=" %%a in ('dir /s/b/ad "%DST_PATH%" ^| sort /r') do rd /q "%%a" 2>nul
rem 这种方式在 cygwin 下也能够正常, 但是一次只能删除目录层次最深的一级, 最简单粗暴的方式就是多执行几遍
for /d /r "%DST_PATH%" %%a in (*) do dir /b/a "%%a" | findstr . >nul || rmdir "%%a"
for /d /r "%DST_PATH%" %%a in (*) do dir /b/a "%%a" | findstr . >nul || rmdir "%%a"
for /d /r "%DST_PATH%" %%a in (*) do dir /b/a "%%a" | findstr . >nul || rmdir "%%a"
获取当前时间戳
# sh
RESULT=$(date +%s)
echo $RESULT
rem bat
call :GetUnixTime RESULT
echo %RESULT%
exit /b 0
:GetUnixTime
setlocal enableextensions
for /f %%x in ('wmic path win32_utctime get /format:list ^<nul ^| findstr "="') do (
set %%x)
set /a z=(14-100%Month%%%100)/12, y=10000%Year%%%10000-z
set /a ut=y*365+y/4-y/100+y/400+(153*(100%Month%%%100+12*z-3)+2)/5+Day-719469
set /a ut=ut*86400+100%Hour%%%100*3600+100%Minute%%%100*60+100%Second%%%100
del /s/q TempWmicBatchFile.bat >nul 2>&1
endlocal & set "%1=%ut%" & goto :EOF
获取文件大小
# sh
RESULT=`du -b "$FILE_PATH" | awk '{print $1}'`
echo $RESULT
rem bat
for /f "delims=" %%a in ("%FILE_PATH%") do set RESULT=%%~za
echo %RESULT%
其他注意点
sh
shell 差异
撸 sh 脚本时最好尽量符合 POSIX 标准, 目前碰到的主要差异有,
bash 和 dash 的差异, GNU 和 BSD 部分命令的差异
中括号变量比较
if [[ $my_var = "" ]]; then
echo "empty"
fi
中括号似乎并不是所有 shell 都支持, 推荐的做法是用 test
if test "x$my_var" = "x" ; then
echo "empty"
fi
对于目录的处理
以 cp 为例, 建议这么做
cp -r src0/src1/. dst0/dst1/
注意 src 末尾的 /.
和 dst 末尾的 /
,
不按照这么写的话, 在不同 shell 下可能会有意外的结果
bat
转义
没错, 批处理就是能把这么基本的东西也搞屎, 关于转义可以参考 这里,
这边也抄一份以防万一:
% %% May not always be required in doublequoted strings, just try
^ ^^ May not always be required in doublequoted strings, but it won't hurt
& ^&
< ^<
> ^>
| ^|
' ^' Required only in the FOR /F "subject" (i.e. between the parenthesis), unless backq is used
` ^` Required only in the FOR /F "subject" (i.e. between the parenthesis), if backq is used
, ^, Required only in the FOR /F "subject" (i.e. between the parenthesis), even in doublequoted strings
; ^;
= ^=
( ^(
) ^)
! ^^! Required only when delayed variable expansion is active
" "" Required only inside the search pattern of FIND
\ \\ Required only inside the regex pattern of FINDSTR
[ \[
] \]
" \"
. \.
* \*
? \?
引号
set v="123"
set v1="%v%"
rem 最终 v1 会是 ""123""
rem 要确保正确的话, 最好这样:
set v2="%~v%"
建议设置
建议批处理文件开头都添加以下内容:
rem 默认关掉恼人的 echo
@echo off
rem 不加的话, 脚本内的变量会影响外部变量, 又是一个蛋疼的默认行为
setlocal
rem 如果想在 for 循环内修改并访问变量, 必须添加该设置,
rem 并且使用 !VAR! 访问变量, 否则变量修改不会生效
setlocal enabledelayedexpansion
转载请注明来自: http://zsaber.com/blog/p/123
既然都来了, 有啥想法顺便留个言呗? (无奈小广告太多, 需审核, 见谅)