shell哲学
shell中一切皆为表达式
shell没有数据类型的概念,所有的变量值本质上都以字符串进行存储。
The shell does not care about types of variables' data; they may store strings, integers, real numbers - anything you like
In truth, these are all stored as strings, but routines which expect a number, a boolean, etc can treat them as such.
至于具体的类型,Shell解释器会根据上下文去确定。eg1.
#!/bin/sh
# RESULT是个字符串
RESULT="false"
# RESULT此时为bool
if $RESULT; then
echo "Is true."
else
echo "Is false."
fi
eg2.
[root@VM-165-116-centos ~]# a="0"
[root@VM-165-116-centos ~]# if [ "$a" != 0 ]; then echo hhh; fi
[root@VM-165-116-centos ~]# if [ "$a" == 0 ]; then echo hhh; fi
hhh
[root@VM-165-116-centos ~]# if [ "$a" -eq 0 ]; then echo hhh; fi
hhh
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# ((a++))
[root@VM-165-116-centos ~]# echo $a
1
sh在解析字符的时候,“;” 等同 回车
根据表达式的定义,任何表达式都必须有一个值。因此shell一切皆表达式的设计原则,确定了shell在执行任何东西(注意是任何东西,不仅是命令)的时候都会有一个返回值。在shell编程中,这个返回值也限定了取值范围:0-255。0为真(true),非0为假(false)。例如,我们可以使用关键字$?来查看上一个执行命令的返回值
➜ ~ echo hhh
hhh
➜ ~ echo $?
0
➜ ~
Accessing Values
We can access the value stored in a variable by prefixing its name with the dollar sign $
. When the shell sees a $
, it performs the following actions:
-
Reads the next word to determine the name of the variable.
-
Retrieves the value for the variable. If a value isn't found, the shell uses the empty string "" as the value.
-
Replaces the $ and the name of the variable with the value of the variable.
基本语法
quote
Strong Quoting with the Single Quotes
When you need to quote several character at once, you could use several backslashes:
% echo a\ \ \ \ \ \ \ b
(There are 7 spaces between 'a' and 'b'.) This is ugly but works. It is easier to use pairs of quotation marks to indicate the start and end of the characters to be quoted:
% echo 'a b'
Inside the single quotes, you can include almost all meta-characters:
% echo 'What the *heck* is a $ doing here???'
What the *heck* is a $ doing here???
The above example uses asterisks, dollar signs, and question marks meta-characters. The single quotes should be used when you want the text left alone. Note - If you are using the C shell, the "!" character may need a backslash before it. It depends on the characters next to it. If it is surrounded by spaces, you don't need to use a backslash.
strong quotes 所见即所得,像golang的`xx`,python的```xx```
Weak Quotes with the Double Quotes
Sometimes you want a weaker type of quoting: one that doesn't expand meta-characters like "*" or "?," but does expand variables and does command substitution. This can be done with the double quote characters:
% echo "Is your home directory $HOME?"
Is your home directory /home/barnett?
% echo "Your current directory is `pwd`"
Your current directory is /home/barnett
# This next example won't work in the C shell and Bourne shell
% echo "Your current directory is $(pwd)"
Your current directory is /home/barnett
字符串
比较
对比字符串只能使用==、<、>、!=、-z、-n
使用[ == ]
对比字符串时,需要在首尾附加额外字符以避免报错。如if [ "1是"",会翻译成if [ == "ab" ],会报语法错误。或者使用[[ ]],就不需要x了。
使用<
或者>
时,如果是用[ ],需要用转义符"",如>
-z
, is_zero,判断长度为0
-n
, 判断长度不为0
截取,替换
${strvar:startindex:lenth}
截取
${strvar/substring/replacement}
使用substring
${strvar//substring/replacement}
使用substring
数字
比较
对比数字使用既能使用-eq、-ne、-gt、-ge、-lt、-le,也能使用==、<、>、!=
算数运算
最简单的做法是,把符合C语言语法的表达式放到(())
中。这时,(())
中的变量无需带上$
➜ ~ echo $((1+2))
3
➜ ~ ((i=1+3))
➜ ~ echo $i
4
➜ ~ ((i++))
➜ ~ echo $i
5
➜ ~
if 分支结构
if list; then list; elif list; then list; ... else list; fi
其中,list是若干个使用管道,;
,&
,&&
,||
这些符号串联起来的shell命令序列
可以简单的理解为,if后面跟的就是个shell命令
if语法中后面最常用的命令是[]
。请注意,[]
是一个命令——test
命令(有对应的二进制文件),也就是说[]
类似test
的语法糖。这也是shell编程容易犯错的地方之一,如果在一开始接触shell编程的时将[]当成if语句的语法结构而不是命令,写[]的时候就经常会里面不写空格,即:
# 正确的写法
if [ $ret -eq 0 ]
# 错读的写法
if [$ret -eq 0]
# 另外,以下写法等价
if [ $ret -eq 0 ]
if test $ret -eq 0
test expression
可以进行数值、字符和文件三个方面的测试
当 test
判断 expression 成立时,退出状态为 0,否则为非 0 值
test命令也可以简写为[],它的语法为[ expression ]
while/until 循环结构
while list-1; do list-2; done
until list-1; do list-2; done
以下脚本可用于检测目标机器是否ping通
#!/bin/sh
IPADDR='8.8.8.8'
INTERVAL=1
while true
do
# if ping failed, the command `ping -c 1 $IPADDR &> /dev/null` return 1
while ping -c 1 $IPADDR &> /dev/null
do
echo "$IPADDR ping ok!"
sleep $INTERVAL
done
echo "$IPADDR ping error! " 1>&2
# loop sleep until ping succeed
until ping -c 1 $IPADDR &> /dev/null
do
sleep $INTERVAL
done
echo "$IPADDR ping ok!"
done
case 分支结构
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
eg.
case $1 in
pattern)
list
;;
pattern)
list
;;
...
esac
for 循环结构
for name [ [ in [ word ... ] ] ; ] do list ; done
eg.
➜ ~ for i in 1 2 3; do echo $i ; done
1
2
3
➜ ~ a=(1 2 3) && for i in ${a[@]}; do echo $i ; done
1
2
3
➜ ~
利用@或*,可以将数组扩展成列表
然后使用#来获取数组元素的个数,格式如下:
${#array_name[@]}
${#array_name[*]}
两种形式是等价的
获取命令行/脚本的入参
使用getopt
getopt是一个外部命令,支持短选项和长选项
Each short/long option character in shortopts may be followed by one colon to indicate it has a required argument, and by two colons to indicate it has an optional argument.
A simple short option is a '-' followed by a short option character.
- If the option has a required argument, it may be written directly after the option character or as the next parameter (i.e. separated by whitespace on the command line). such as
-p3306
or-p 3306
- If the option has an optional argument, it must be written directly after the option character if present. such as
-p3306
A long option normally begins with '--' followed by the long option name.
- If the option has a required argument, it may be written directly after the long option name, separated by '=', or as the next argument (i.e. separated by whitespace on the command line). like
–-clong=arg
or–-clong arg
- If the option has an optional argument, it must be written directly after the long option name, separated by '=', if present (if you add the '=' but nothing behind it, it is interpreted as if no argument was present; this is a slight bug). like
–-clong=arg
#!/bin/sh
# 处理参数,规范化参数。:表示该opt需要parameter
ARGS=`getopt -o a:s: --long agentnames:,serverips:,idcfuncname: -- "$@"`
if [ $? != 0 ];then
echo "Parse args err. Terminating..."
exit 1
fi
# 重新排列参数顺序,组织成($1, $2, ...)
eval set -- "${ARGS}"
# 通过shift和while循环处理参数
while true
do
# $1 表示 option 名,$2 表示option的value
case $1 in
# 安装的目标agent
-a | --agentnames)
agent_list=($2)
shift
;;
# server ip,包含主备两个
-s | --serverips)
# should contain a server master ip and a server slave ip
server_ips=($2)
num=${#server_ips[@]}
if [ ${num} -ne 2 ]; then
echo "the given server ip num is ${num}, not eq 2"
exit 1
fi
# 使用环境变量 SERVER_IP 配置 server ip
export SERVER_IP="$2"
shift
;;
# 不同的功能类型可能需要前置执行不同的策略
--idcfuncname)
idcfuncname=$2
case ${idcfuncname} in
ABC )
echo ${idcfuncname}
;;
esac
shift
;;
--)
shift
break
;;
*)
echo "Internal error!"
exit 1
;;
esac
shift
done
# echo "agent_list: ${agent_list[@]}"
# echo "server_ips: ${SERVER_IP}"
# echo "idcfuncname: ${idcfuncname}"
字符串运算
替换
# string中第一个old替换为new
${string/old/new}
# string中所有old替换为new
${string//old/new}
# string从下标n到结尾的子串
${string:n}
# string从下标n开始长度为m的子串
${string:n:m}
# string从下标0开始长度为m的子串
${string::m}
数组
普通数组
a=() # 空数组
a=(1 2 3) # 元素为1,2,3的数组
echo ${#a[*]} # 数组长度
echo ${a[2]} # 下标为2的元素值(下标从0开始)
a[1]=0 # 给下标为1的元素赋值
# 遍历数组
for i in ${a[*]}
do
echo ${i}
done
unset a # 清空数组
换行符处理
如果命令执行结果有多行内容,存入变量并打印时换行符会丢失。给echo 加上引号能够保持原样
[root@VM-centos ~]# echo $(free)
total used free shared buff/cache available Mem: 16132456 3998044 1187960 296480 10946452 11708584 Swap: 0 0 0
[root@VM-centos ~]# echo "$(free)"
total used free shared buff/cache available
Mem: 16132456 3987904 1193392 296480 10951160 11707856
Swap: 0 0 0
[root@VM-165-116-centos ~]#
并发
使用&
和wait
可以实现并发执行任务
wait is a BASH built-in command. From man bash:
wait [n ...]
Wait for each specified process and return its termination sta-
tus. Each n may be a process ID or a job specification; if a
job spec is given, all processes in that job's pipeline are
waited for. If n is not given, all currently active child pro-
cesses are waited for, and the return status is zero. If n
specifies a non-existent process or job, the return status is
127. Otherwise, the return status is the exit status of the
last process or job waited for.
eg.
workhard &
[1] 27408
workharder &
[2] 27409
wait %1 %2 (or wait, or wait 27408 27409)
wait
waits for the child process of the current shell to be done
https://stackoverflow.com/questions/13296863/difference-between-wait-and-sleep
nohup 和 & 的区别
&
使得命令在后台以子进程方式运行,但是一旦当前shell terminated,所有的这些子进程也会挂,因为它们会收到当前shell发来的SIGHUP信号。而nohup可以捕捉SIGHUP signal,这样这些子进程就永远收不到SIGHUP,即使当前shell terminated它们也一无所知地正常运行下去
&
would run the process in background using a subshell. If the current shell is terminated (say by logout), all subshells are also terminated so the background process would also be terminated.
when running a command using &
and exiting the shell afterwards, the shell will terminate the sub-command with the hangup signal (kill -SIGHUP <pid>). This can be prevented using nohup, as it catches the signal and ignores it so that it never reaches the actual application.
In case you're using bash, you can use the command shopt | grep hupon
to find out whether your shell sends SIGHUP to its child processes or not. If it is off, processes won't be terminated, which means sub-shells can still be alive while shell is terminated
refers: https://stackoverflow.com/questions/15595374/whats-the-difference-between-nohup-and-ampersand
网友评论