贡献者: addis
echo "..." 先输出来看看,而不是真的执行。看看变量展开是否和预期的一致(就像 make 一样)。
.sh 的文本文件,也可以没有。
./name.sh 参数1 参数2 也可以用 source /path/name.sh 参数1 参数2(source 也可以简写成 .。sh shell 中只能用后者。), 前者在一个新的 shell process 中运行然后退出(环境变量之类的改动在退出会消失), 后者在当前的 shell process 运行, 相当于直接把文件中的内容复制粘贴到命令行执行.
; 隔开两个命令,第一个失败了第二个仍然会运行,但 && 隔开则不会(这个其实是逻辑与算符)。
&& 后面可以直接换行,不需要加 \
> 把 shell 中的 stdout 导入文件.
#! 指定运行使用的 shell,如 #!/bin/bash
exit 结束 script,exit 0 声明运行成功
# 注释
var=xxx,等号两边不能有空格。$var 或者 ${var} 获取它的值。例如 abc$var 或者 abc${var}def。后者的花括号不能省略。
${var:2:5} 可以获取第 3 个字符开始的 5 个字符。
${var/旧文字/新文字} 可以对变量的字符串进行替换
${var:-"默认字符串"} 如果 var 未定义或为空,则输出 默认字符串。
var 以 字符串 结束,${var%字符串} 移除最后的 字符串。例如 ${0%/*} 获取当前脚本所在的目录
var 以 字符串 开始,${var#字符串} 移除最前面的 字符串
varname=myvar,那么 ${!varname} 相当于 ${myvar}。如果 varname 是第一个参数,那么就用 ${!1}
$(命令) 或者 `命令` 相当于把 命令 的输出展开到当前位置,如 echo `pwd -P`。
bins="123 234"
exec 文件名.sh 执行另一个脚本。
1>&2,例如 echo bar 1>&2。
./文件名.sh > 123.log 只会把 stdout 导入文件,而不包含 stderr。还要在后面加上 2>&1。或者直接用 ./文件名.sh &> 123.log。
"path/to/other_script.sh"(待验证)
/bin/bash -c "命令"
命令 "abc def" 或者 命令 'abc def' 中,abc def 是一个参数,作为字符串传给 main() 函数的 argv[1],不包含引号。如果不加引号,abc 和 def 就分别是 argv[1] 和 argv[2]。
"..." 中的 $变量 会展开,但是 '...' 中不会
'...' 就是绝对的 string literal,里面不会发生任何转义
'...' 中 escape 单引号,不能直接做到,可以用 'it'\''s here.' 其中三部分分别是 'it',\','s here.'。
echo 'abc\'def'gh' 会输出 abc\defgh
main(),那么就 escape:\" 或者 \'
\ 可以用 \\ 来 escape
$var、${var} 等展开。
命令 | 命令2 使用 pipe 把一个命令在 stdout 的输出作为另一个命令的 stdin 输入。特别注意 命令2 是在 subshell 执行的。如果在该 subshell 执行多个命令,可以用 命令 | { 命令2 ; 命令3 } 例如 echo 123 | { read line; echo "The line is: $line"; } 又例如 printf "123\n234\n" | while read line; do echo "The line is: $line"; done 中 while 可以看成一个命令。
echo 123;
if [ $? -ne 0 ]; then # 判断 exit status
...
fi
# 判断路径是否存在(感叹号后面一定要有空格)
if ! [ -d "$repo" ]; then
echo "directory not found!" 1>&2
fi
# 判断文件是否存在且不是文件夹或者特殊文件
if ! [ -f "$fname" ]; then
echo "file not found!" 1>&2
fi
# 判断相等
if [ $i -eq 3 ]
...
elif [ $i -ne 3 ]
...
fi
if 的简写如 [ -e 文件 ] && mv 文件 新文件。例子:for file in file1 file2 file3; do [ -e "$file" ] && mv "$file" destination_directory done
-f, -d,还有 -z(判断字符串是否为空),-n(判断字符串是否为非空),例如 if [ -n $(git remote | grep github) ]。 -e 判断是否是文件(包括文件夹和特殊文件),-h 判断是否为 symlink,file1 -nt file2(newer than),file1 -ot file2(older than),-r, -w, -x 是否是文件且具有读写执行权限。
if [ string1 = string2 ] 判断字符串是否相等,string1 != string2 判断是否不相等。
-z 检查后面的内容是否为空.
-n 检查是否为非空
-v 检查变量是否有定义(定义为空字符也算)
双方括号不属于 POSIX 标准而是 bash 和 zsh 等特有的。语法更为现代
if [[ $a == $b ]]; then 判断相等,另有 !=, <, > 等。注意 <= 注意比较的不是数值大小而是字符串排序大小。
if [[ "字符串" =~ 正则表达式 ]] 可以判断字符串是否匹配正则表达式。其中正则表达式的格式和 grep -E 中的是一样的。例如 if [[ "$var" =~ ^-?[0-9]+$ ]] 判断变量是否表示整数。
# 判断文件是否为空
if [ -s 文件名 ]; then
# The file is not-empty.
else
# The file is empty.
fi
# 比较数字大小
if (( a > b )); then
...
fi
#!/bin/bash
for ((i = 1; i < 5; ++i))
# for i in {1,2,3,4} # 等效
# for i in 1 2 3 4 # 等效
do
printf "\nfile${i}.txt\n"
done
如果在 printf 语句后面加上 && break,当前者失败时就会跳出循环。
另外 continue 也可以跳出循环
在当前目录中所有子目录循环
for d in */ ; do
d=${d%/} # 删除目录名最后的斜杠
echo "$d" # 打印所有子目录名
done
文件循环
for file in *; do
if [ -f "$file" ]; then
echo "$file"
fi
done
#!/bin/bash
arr=(1 3 hello 13)
for ((i=0;i<4;++i))
do
printf "arr[${i}] = ${arr[i]}\n"
done
例子
#!/bin/bash
# 确保文件最后有两个空格
# use `find . -type f -name "*.matt" -exec ./convert_matt.sh {} \;`
# to convert subfolder
file=${1}
printf "${file}... "
c2=$(tail -c 2 ${file})
if [ "${c2}" == " " ]
then
printf "already in new format\n"
else
printf " " >> ${file}
printf "done\n"
fi
$0 当前 shell script 的文件名,例如调用脚本的命令是 some/../path/test.sh,那就返回这串字符。如果用 source some/path/test.sh 或者直接在命令行打 $0,那么就返回 bash。
$@ 返回所有 shell arguments
$? 上一个命令的 exit status,0 代表成功,否则失败
readlink -f "$0" 获取脚本绝对路径(包括脚本文件名)(不可以直接在命令行使用,或者 source 脚本.sh 时使用)
dirname $(readlink -f "$0") 获取脚本所在绝对目录(不包括脚本文件名)
函数名() {
...
}
$1, $2 或者 ${1}, ${2},例如
greeting() {
echo "Hello $1"
}
greeting "Joe"
$0 是调用脚本的命令(也就是 argv[0])。$# 可以查看 args 的总个数
一个用于确保执行成功的函数:
check_failure() {
if [ $1 -ne 0 ]; then
echo "Error! Exiting..."
exit 1
fi
}
cmd1 arg1 && cmd2 arg2
check_failure $?
函数通过返回的是 exit code,例如
myfunc() {
if [[ -f "$1" ]]; then
return 0 # success
else
return 1 # failure
fi
}
myfunc "/etc/passwd"
echo $? # prints 0 if file exists, 1 if not
若要返回数据,就用 stdout,例如
sum() { echo $(( $1 + $2 )) }
result=$(sum 10 32)
echo "Sum is: $result" # → 42
也可以用 global var,如
get_name() { name="Alice" }
get_name
echo "$name" # Alice
默认情况下,所有函数本地变量都是全局变量,除非声明了 local。如 bar() { local var="hi" }。
return 等效于 return 0。
return 关键字,那么函数的 exit code 就是最后一个命令的 exit code。例如 command_exist() { command -v "$1" &> /dev/null } 可以用于检查一个命令是否存在(包括 alias)。用法:command_exist ls; echo $?。如果存在则打印 0,因为成功的退出码是 0。
lsb_release -rs 可以获取 ubuntu 的系统版本号(例如 18.04)
getconf _NPROCESSORS_ONLN 可以查看系统有多少逻辑 cpu