Description
shell
变量与变量展开
hello=world
echo $hello
hello="i am world"
echo $hello
echo hello world
echo -n "enter your name:" (-n去掉行尾的回车)
read name
echo $name
- $是取变量的值,所以总是出现在=号的右边
- =号前后不能有任何空格
- =号后面的字符串如果不包含空格可以不加""
- echo后面字符串即使包含空格也可不加"",
如果要正常输出变量中的换行符,则必须加双引号
- shell视所有变量为字符串类型.
- 单引号不展开变量, 仅原样输出字符串, 双引号会展开变量.
- 强制展开变量的办法:嵌套使用单引号,或者整体使用双引号或者拼字符串
$ sed -n '1 c$HOME' package.json(不会展开变量)
$HOME
$ sed -n "1 c$HOME" package.json(整体使用双引号)
/home/cyper
$ sed -n '1 c'$HOME'' package.json(嵌套使用单引号)
/home/cyper
$ sed -n '1 c'$HOME package.json(拼字符串)
/home/cyper
输出重定向
3个特殊的文件描述符0 stdin, 1 stdout, 2 stderr.
格式:command FILE_DESCRIPTOR>outputFileName
foo 1>ok.txt 2>error.txt
因为foo命令不存在,所以出错信息输出到了error.txt中,注意到同时生成了一个空的ok.txt文件, 其中标准输出重定向1>
可以简写成>
怎么将正常的及错误的消息输出到同一文件foo 1>ok.txt 2>&1
, 即使用&1
来表示文件描述符为1对应的输出文件,在这里它们同时指向ok.txt.
引号
'原样输出
"会先做expansion
参数
$HOME 打印出/home/cyper
$0: shell script的名字
$#: 参数个数
$$: 运行脚本时的process ID
$1,$2, ...传递给脚本的参数
$*: 所有参数使用$IFS分隔(默认为空格)
$@: 同$*但总是使用空格分隔
$?: 命令的退出状态,0成功,非0失败
PS1,PS2: 命令提示符
快速测试
$ IFS=’‘
$ set foo bar bam
$ echo “$@“
foo bar bam
$ echo “$*“
foobarbam
$ unset IFS
$ echo “$*“
foo bar bam
条件测试
if test -f fred
then
...
elif test -d fred; then
...
else
...
fi
或
if [ -f fred.c ]
then
...
fi
或
if [ -f fred.c ]; then
...
fi
[]和其中包含的测试条件必须用空格隔开
条件比较
string comparison | result |
---|---|
str1 = str2 | string equal |
str1 != str2 | string not equal |
-n str | string is not null |
-z str | string is null(an empty string) |
num1 -eq num2 | exprs are equal |
eq, ne, gt, ge, lt, le | ... |
! expr1 | is false? |
-d file | is directory? |
-e file | exist? |
-f file | is file? |
-r file | is readable? |
-w file | is writable? |
-x file | is executable? |
-g file | if set-group-id is set on file? |
-u file | if set-user-id is set on file? |
You may be wondering what the set-group-id
and set-user-id
(also known as set-gid
and set-uid
) bits are. The set-uid
bit gives a program the permissions of its owner, rather than its user, while the set-gid
bit gives a program the permissions of its group.The bits are set with chmod
,using the s
and g
options.The set-gid and set-uid flags have no effect on files containing shell scripts, only on executable binary files.
特殊情况
if [ $timeofday = "yes" ]这段可能有问题, 当timeofday为空的时候if [ = "yes"]语法不合规,最好写成if [ "$timeofday" = "yes" ]
for
for i in foo bar 43
do
echo $i
done
或
for i in $(ls f*.sh); do
lpr $file
done
while
echo "Enter password"
read trythis
while [ "$trythis" != "secret" ]; do
echo "sorry, try again"
read trythis
done
util
who查看当前系统有哪个登录用户, 比如who|grep cyper返回0, 而who|grep green返回1, 这里我们只关心script的执行情况(0 or 1), 不关心stdout.
# 如果参数1对应的用户没有出现, 就等待60s后再次检测
until who | grep "$1" > /dev/null
do
sleep 60
done
# 响铃
echo -e '\a'
echo "**** $1 has just logged in ****"
exit 0
case
基本结构
case "$x" in
pattern | pattern2 ) statements;;
* ) echo "default case";;
esac
例子:
#!/bin/sh
echo "Enter your name?"
read timeofday
case "$timeofday" in
yes | y | Yes | YES )
echo "Good morning"
echo "It's cool"
;;
[nN]*)
echo "Good afternoon"
;;
*)
echo "sorry"
exit 1
;;
esac
exit 0
AND
打印出hello in else
touch file_one
rm -rf file_two
if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo " there"
then
echo "in if"
else
echo "in else"
fi
OR
打印出hello in if
rm -f file_one
if [ -f file_one ] || echo "hello" || echo "there"
then
echo "in if"
else
echo "in else"
fi
Statement Blocks
可以用AND/OR选择性的执行语句块
get_confirm && {
echo "hello"
echo "dome some other thing"
}
braces and brackets
braces()中的command在subshell中执行,在期间修改的env var不会影响current shell(比如执行了cd命令).在brackets{}中包含的command在当前shell中执行.
set -e
遇到错误即退出, 不继续执行后续脚本
set -o noclobber
打开此选项是为了防止使用重定向符>
意外覆盖文件,如果要强制覆盖,使用>|
.
Here document
用来快速提供多行输入,常用于交互式命令比如telnet, ftp.
command << terminaor [input ...]
或者
command <<- terminator [input ...]
每次读一行,直到读到某行是terminator时结束,<<-
会把input中每行前面的tabs去掉。
登录到一个远程机器并把某文件mail给本机。
$ telnet << END
> open 190.100.2.1
> gregl password
> mail -s "remote userlogs" gregl@190.100.2.2 < /var/log/userlog
> quit
> END
substitution
文件名替换: 使用w*,?,[a-z],
命令替换:
* 0或多个字符
? 单个字符
[a-z0-9] 范围内单个字符
[!0-9] 范围外单个字符,和正则^不一样
$(command) 执行command并把输出结果作为其它命令的参数
$(< filename) 将filename的内容作为其它命令的参数
echo '*' 如果需要转递特殊字符做为参数,需要使用引号或\以防止该符号被shell解释.
echo \* 同上
echo "*" 同上
Date
DATE=`date +%Y-%m-%d`
$(date +%F)
DATE=`date +%Y-%m-%d:%H:%M:%S`
其中%Y-%m-%d <==> %F, 输出2017-04-26