shell避坑指南
作者:互联网
约定
- Error:错误写法;
- Bad:是正确写法,但不是推荐写法;
- Good:不仅是正确写法,而且是推荐的写法。
空格避坑
避坑:带有空格的字符串在进行判断操作时要加引号
示例:
str="this is a example"
[ -n ${str} ] # Error: 单中括号不能正确处理有空格的情况
[ -n "${str}" ] # Good: 加引号后可以正确处理
[[ -n ${str} ]] # Bad: 双中括号可以正确处理
[[ -n "${str}" ]] # Good: 双中括号虽然可以正确处理,但建议还是加引号
避坑:带有空格的字符串在进行路径操作时要加引号
示例:
mkdir -p test 1/test 2/test3 # Error
mkdir -p "test 1"/"test 2"/test3 # Good:建议分开加引号
mkdir -p "test 1/test 2/test3" # Bad
cd "test 1"/"test 2"/test3 # Good:建议分开加引号
cd "test 1/test 2/test3" # Bad
rm -rf "test 1" # Good
rm -rf "test 1"/"test 2" # Good:建议分开加引号
rm -rf "test 1/test 2" # Bad
避坑:带有空格的字符串在进行"/*"路径操作时要加引号
示例:
# 移动
mv "test 1/*" test2 # Error:带有空格的路径,进行/*操作时这样写错误
mv "test 1"/* test2 # Good
# 复制
cp "test 1/*" test2 # Error:同理
cp "test 1"/* test2 # Good
# 删除
rm -rf "test 1/*" # Error:无法删除`test 1`目录下的内容
rm -rf "test 1"/* # Good
避坑:含有通配符的*的字符串在操作时不可加引号,否则无法正常工作
rm -rf /home/zdy/Ascend-mindxedge*.tar.gz # Good
rm -rf /home/zdy/"Ascend-mindxedge*.tar.gz" # Error:无法删除
rm -rf /home/zdy/Ascend-mindxedge* # Good
rm -rf /home/zdy/"Ascend-mindxedge*" # Error:无法删除
删除文件避坑
避坑:rm -rf ${path}/*时要确保${path}存在,否则变砖
说明:
如果${path}
为空,则rm -rf ${path}/*
变为rm -rf /*
,会把根目录下的所有文件删完,变砖。
避坑:rm -rf删除目录软连接时不要加末尾斜杠
示例:
# 假定test是指向目录的软连接
rm -rf test # Bad:可以删除test软连接,而不影响真实目录
rm -rf test/ # Error:不会删除test软连接,而是删除test指向目录下的所有文件
rm -f test # Bad:可以删除test软连接,而不影响真实目录
rm -f test/ # Error:无法删除,提示:rm: cannot remove 'test/': Is a directory
unlink test # Good
函数调用避坑
避坑:函数只能返回数字,不能返回字符串
示例:
function fun1() { # Good
return 1
}
function fun2() { # Bad
return "1"
}
function fun3() { # Error
return "T"
}
说明:
shell不区分数字和字符串,所有内容都是字符串。所以fun1和fun2,return都会解释为返回数字;fun3的"T"
无法解释为数字,错误。
避坑:函数直接返回其它函数需要用$(fun)
function fun1() {
return 100
}
function fun2() { # Error:「return: fun1: numeric argument required」
return fun1
}
function fun3() { # Good:这种写法可以准确的返回fun1的错误码100
return $(fun1)
}
function fun4() { # Bad
fun1
return $?
}
function fun5() { # Good
fun1
local ret=$?
# do something ...
return ${ret}
}
function fun6() { # Good:做一层封装,会丢失fun1的错误码,
if fun1; then # 如果只想判断fun1是否执行成功而不关心系错误码可用此方法
return 0
else
return 1
fi
}
避坑:在if中判断函数是否执行成功,不要加$(fun)
# case 1
if myfun arg1 arg2; then # Good:直接调用
echo "function exe success"
# do something
else
echo "fun exe failed"
# do something
fi
# case 2
if $(myfun arg1 arg2); then # Bad:某些情况下会失败
echo "function exe success"
# do something
else
echo "fun exe failed"
# do something
fi
注意:
shell命令执行成功返回0,失败为非0。因此用if myfun ...
这种方式,必须保证myfun
在执行成功时返回0。
避坑:同时获取函数执行结果和执行状态时,注意local用法避坑
function test() {
echo "here"
return 1
}
# case 1
function main() {
local value=$(test) # Error:value能捕获到test的运行结果"here",但$?始终是0,不能捕获到test返回值1
if (($? == 0)); then
echo "right"
else
echo "error"
fi
}
# case 2
function main() {
local value
value=$(test) # Good:value能捕获到test的运行结果"here",$?也能捕获到test返回值1
if (($? == 0)); then
echo "right"
else
echo "error"
fi
}
# case 3
function main() {
value=$(test) # Bad:value能捕获到test的运行结果"here",$?也能捕获到test返回值1,但用的是全局变量,不推荐
if (($? == 0)); then
echo "right"
else
echo "error"
fi
}
main
运算符避坑
避坑:"-eq"的含义不完全等于"=="
# case 1
[[ 1 == 1 ]] # Bad
echo $? # 0
# case 2
[[ 01 == 1 ]] # Error
echo $? # 1
# case 3
[[ 01 -eq 1 ]] # Good
echo $? # 0
# case 4
[[ "01" -eq "1" ]] # Bad
echo $? # 0
# case 5
((01 == 1)) # Good
echo $? # 0
# case 6
(("01" == "1")) # Bad
echo $? # 0
说明:
- 在shell中一切变量其实都是字符串,并没有数字这个概念,例如:
var=1
和var="1"
,本质上是没有区别的。所以为了进行算数运算,一般都要用expr
、let
等命令;为了进行算数比较,一般要用-eq
、-ne
等比较运算。这些命令和运算符会自动把字符串解释成数字。因此case 3和case 4其实是等价的; ==
既可以用来判断字符串是否相等,又可以用来判断数字是否相等。为了不混淆,如果明确要判断数字,最好用-eq
而不是==
,或者用case 5的方式;如果明确要字符串比较最好用[[ "str1" == "str2" ]]
。对于-ne
、-le
等其它算数运算符原则上类同-eq
。
避坑:字符串空和非空判断
var1=
[[ -z" ${var1}" ]] # 结果为0
[[ -n "${var1}" ]] # 结果为1
var2=""
[[ -z "${var2}" ]] # 结果为0
[[ -n "${var2}" ]] # 结果为1
var3="ABC"
[[ -z "${var3}" ]] # 结果为1
[[ -n "${var3}" ]] # 结果为0
说明:
在shell中var1=
和var2=""
的含义是一样的。因为shell中所有变量都为字符串,而且可加引号也可不加引号。
避坑:变量是否被定义
# var1未被定义
var1=
echo ${var1:+word}
echo ${var1}
# var2未被定义
var2=""
echo ${var2:+word}
echo ${var2}
# var3被定义
var3="ABC"
echo ${var3:+word}
echo ${var3}
说明:
在shell中var1=
和var2=""
的含义是一样的。因为shell中所有变量都为字符串,而且可加引号也可不加引号。
避坑:shell不支持小数运算,若要进行小数运算需要借助bc或awk
软连接避坑
避坑:当ln的目标是目录时,要防止循环指向
# Error
mkdir /root/test_dir
# 第一次执行soft_test指向test_dir
ln -sf /root/test_dir /root/soft_test
# 再次执行会导致test_dir下创建一个这样的软连接:test_dir -> /root/test_dir,导致循环指向
ln -sf /root/test_dir /root/soft_test
# Good
mkdir /root/test_dir
# 第一次执行soft_test指向test_dir
ln -sfn /root/test_dir /root/soft_test
# 再次执行,不会导致循环指向
ln -sfn /root/test_dir /root/soft_test
原因:
加上-n
参数,会把test_dir看做一个文件,而不是目录,则不会进入test_dir下创建软连接。如果不加-n,则test_dir是目录,创建软连接时会进入这个目录,并在它下边创建软连接。
推荐写法
字符串比较
# 相等
if [[ "${string1}" == "${string2}" ]]; then
echo "The two strings are the same"
fi
# 不等
if [[ "${string1}" != "${string2}" ]]; then
echo "The two strings are different"
fi
# 为空
if [[ -z "${string}" ]]; then
echo "empty string"
fi
# 非空
if [[ -n "${string}" ]]; then
echo "string is not empty"
fi
# ${substring}在${string}中
if [[ "${string}" == *"${substring}"* ]]; then
echo "${string} contains: ${substring}"
fi
# ${substring}在${string}中
if [[ "${string}" =~ "${substring}" ]]; then
echo "${string} contains: ${substring}"
fi
数值比较
# 相等
if ((int1 == int2)); then
echo "equal"
fi
# 不等
if ((int1 != int2)); then
echo "not equal"
fi
# 大于
if ((int1 > int2)); then
echo "greater"
fi
# 大于等于
if ((int1 >= int2)); then
echo "greater or equal"
fi
# 小于
if ((int1 < int2)); then
echo "lesser"
fi
# 小于等于
if ((int1 <= int2)); then
echo "lesser or equal"
fi
标签:指南,shell,避坑,echo,Good,fi,test,rm 来源: https://www.cnblogs.com/sinicheveen/p/16502101.html