基本命令
echo
echo 命令用于在 shell 中打印 shell 变量的值,或者直接输出指定的字符串
语法
echo(选项)(参数) 选项
-e:激活转义字符。使用 -e 选项时,若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
\a 发出警告声; \b 删除前一个字符; \c 最后不加上换行符号; \f 换行但光标仍旧停留在原来的位置; \n 换行且光标移至行首; \r 光标移至行首,但不换行; \t 插入 tab; \v 与\f 相同; \ 插入\字符; \nnn 插入 nnn(八进制)所代表的 ASCII 字符;
案例
Foreground 文字颜色
颜色码:重置=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37
echo "\033[颜色码m XXX \033[0m"
1
2
3
4
5
6
7
8
9
10
11
12
| echo "\033[30m This is 黑色=30 text\033[0m"
echo "\033[31m This is 红色=31 text\033[0m"
echo -e "\033[32m This is 绿色=32 text\033[0m"
echo -e "\033[33m This is 黄色=33 text\033[0m"
echo -e "\033[34m This is 蓝色=34 text\033[0m"
echo -e "\033[35m This is 洋红=35 text\033[0m"
echo -e "\033[36m This is 青色=36 text\033[0m"
echo -e "\033[37m This is 白色=37 text\033[0m"
echo -e "\033[0m This is 重置=0 text\033[0m"
# 设置了颜色后,如果没有[0m重置,后面的文字都会变成设置的颜色
echo -e "\033[31m This is 1.红色=31 text"
echo "这是没加颜色的,会变成红色"
|
注意:
- Mac 可以不用加 -e
- Mac 用的是\033,Linux 用\e(未验证)?
更多颜色见:https://misc.flogisoft.com/bash/tip_colors_and_formatting
Background 背景色
Greed Background 颜色码:重置=0,黑色=40,红色=41,绿色=42,黄色=43,蓝色=44,洋红=45,青色=46,白色=47
1
2
3
4
5
6
7
8
| echo -e "\033[40m 背景黑色=40 \033[0m"
echo -e "\033[41m 背景红色=41 \033[0m"
echo -e "\033[42m 背景绿色=42 \033[0m"
echo -e "\033[43m 背景黄色=43 \033[0m"
echo -e "\033[44m 背景蓝色=44 \033[0m"
echo -e "\033[45m 背景洋红=45 \033[0m"
echo -e "\033[46m 背景青色=46 \033[0m"
echo -e "\033[47m 背景白色=47\033[0m"
|
更多颜色见:https://misc.flogisoft.com/bash/tip_colors_and_formatting
文字闪动
红色数字处还有其他数字参数:0 关闭所有属性、1 设置高亮度(加粗)、4 下划线、5 闪烁、7 反显、8 消隐
echo "\033[37;31;xxxm XXX \033[39;49;0m"
其中小写 xxx,可以替换成上面
1
| echo -e "\033[37;31;5m MySQL Server Stop...\033[39;49;0m"
|
问题
echo -n 在 Mac 不生效
Mac Shell 脚本中使用 echo -n
导入文件无法实现不换行,在 Linux 服务器上未发现此问题。
问题代码:
1
2
3
4
5
6
7
8
9
10
| #!/bin/bash
test_file="tmp"
echo -n "line1" > ${test_file}
echo -n "line2" >> ${test_file}
echo -n "line3" >> ${test_file}
#/bin/echo -n "line1" > ${test_file}
#/bin/echo -n "line2" >> ${test_file}
#/bin/echo -n "line3" >> ${test_file}
|
查看输出的文件 tmp 如下,不能实现不换行并且信息打印有误,多了 -n:
-n line1 -n line2 -n line3
解决方法:
将 echo 替换为 /bin/echo
1
2
3
4
5
| test_file="tmp"
/bin/echo $test_file
/bin/echo -n "line1" > ${test_file}
/bin/echo -n "line2" >> ${test_file}
/bin/echo -n "line3" >> ${test_file}
|
再看输出:
line1lin2line3
if 条件判断
[ x ]
表示条件测试。注意这里的空格很重要。要注意在 [
后面和 ]
前面都必须要有空格- 在 shell 中,then 和 fi 是分开的语句。如果要在同一行里面输入,则需要用分号将他们隔开。
- 注意 if 判断中对于变量的处理,需要加引号,以免一些不必要的错误。没有加双引号会在一些含空格等的字符串变量判断的时候产生错误。比如
[ -n "$var" ]
如果 var 为空会出错 - 判断是不支持浮点值的
- 如果只单独使用
>
或者 <
号,系统会认为是输出或者输入重定向,虽然结果显示正确,但是其实是错误的,因此要对这些符号进行转义 - 空变量和没有初始化的变量可能会对 shell 脚本测试产生灾难性的影响,因此在不确定变量的内容的时候,在测试号前使用 -n 或者 -z 测试一下
条件判断基本结构 if(数字条件,字符串条件,字符串为空)
条件判断的格式
1
2
3
| [ exp ]
[[ exp ]]
test exp
|
注意: exp 与 “[“、”]” 括号之间必须要有空格,否则会报语法错误;
if 基本结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| if 条件判断的格式 ;then
# statement1
# .......
fi
if 条件判断的格式 ;then
statement1
else
statement2
fi
if 条件判断的格式 ; then
statement1
elif 条件判断的格式 ; then
statement2
fi
|
注意 == 或 = 前后要有空格
if
1
2
3
4
5
6
7
8
9
| a=1
#单个if
if [ a==2 ] ; then # ==号前后没有空格,这样会认为是true
echo '1.a==2' # 这里会输出结果
fi
if [ a == 2 ] ; then # 判断结果为false
echo '2.a==2' # 不会输出
fi
|
if else
1
2
3
4
5
6
| PUBLISH_TO_MAVEN=all
if [ $PUBLISH_TO_MAVEN == 'all' ]; then
echo "publish all"
else
echo "publish $PUBLISH_TO_MAVEN"
fi
|
if elif else
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| t=redis
if [[ $t = 'tomcat' ]];
then
echo "Input is tomcat"
elif [[ $t = 'redis' ]] || [[ $t = 'zookeeper' ]];
then
echo "Input is $t"
else
echo "Input Is Error."
fi
或者:
a=1
if [[ a -eq 2 ]]; then
echo "a=2"
elif [[ a -eq 3 ]]; then
echo "a==3"
else
echo "a=1"
fi
|
if 常用判断
文件/目录判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| [ -a FILE ] 如果 FILE 存在则为真。
[ -b FILE ] 如果 FILE 存在且是一个块文件则返回为真。
[ -c FILE ] 如果 FILE 存在且是一个字符文件则返回为真。
[ -d FILE ] 如果 FILE 存在且是一个目录则返回为真。
[ -e FILE ] 如果 指定的文件或目录存在时返回为真。
[ -f FILE ] 如果 FILE 存在且是一个普通文件则返回为真。
[ -g FILE ] 如果 FILE 存在且设置了SGID则返回为真。
[ -h FILE ] 如果 FILE 存在且是一个符号符号链接文件则返回为真。(该选项在一些老系统上无效)
[ -k FILE ] 如果 FILE 存在且已经设置了冒险位则返回为真。
[ -p FILE ] 如果 FILE 存并且是命令管道时返回为真。
[ -r FILE ] 如果 FILE 存在且是可读的则返回为真。
[ -s FILE ] 如果 FILE 存在且大小非0时为真则返回为真。
[ -u FILE ] 如果 FILE 存在且设置了SUID位时返回为真。
[ -w FILE ] 如果 FILE 存在且是可写的则返回为真。(一个目录为了它的内容被访问必然是可执行的)
[ -x FILE ] 如果 FILE 存在且是可执行的则返回为真。
[ -O FILE ] 如果 FILE 存在且属有效用户ID则返回为真。
[ -G FILE ] 如果 FILE 存在且默认组为当前组则返回为真。(只检查系统默认组)
[ -L FILE ] 如果 FILE 存在且是一个符号连接则返回为真。
[ -N FILE ] 如果 FILE 存在 and has been mod如果ied since it was last read则返回为真。
[ -S FILE ] 如果 FILE 存在且是一个套接字则返回为真。
[ FILE1 -nt FILE2 ] 如果 FILE1 比 FILE2 新, 或者 FILE1 存在但是 FILE2 不存在则返回为真。
[ FILE1 -ot FILE2 ] 如果 FILE1 比 FILE2 老, 或者 FILE2 存在但是 FILE1 不存在则返回为真。
[ FILE1 -ef FILE2 ] 如果 FILE1 和 FILE2 指向相同的设备和节点号则返回为真。
|
字符串判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| [ -z STRING ] 如果STRING的长度为零则返回为真,即空是真
[ -n STRING ] 如果STRING的长度非零则返回为真,即非空是真
[ STRING1 ] 如果字符串不为空则返回为真,与-n类似
[ STRING1 == STRING2 ] 如果两个字符串相同则返回为真
[ STRING1 != STRING2 ] 如果字符串不相同则返回为真
[ STRING1 < STRING2 ] 如果 “STRING1”字典排序在“STRING2”前面则返回为真。
[ STRING1 > STRING2 ] 如果 “STRING1”字典排序在“STRING2”后面则返回为真。
-z #zero 当前字符串是否为空
-n #not zero 当前字符串是否不为空
== 两端字符串是否相等,与=等价;如:if [ "$a" == "$b" ]和if [ "$a" = "$b" ]等价
!= 两端字符串是否不相等
> 长度是否大于
< 长度是否小于
|
例如:
1
2
3
4
5
6
7
8
9
10
| STR1="HELLO"
STR2="HELL"
if [[ $STR1 > $STR2 ]];then
echo "1.$STR1 > $STR2" # 输出结果
fi
if [ $STR1 \> $STR2 ];then
echo "2.$STR1 > $STR2" # 输出结果
fi
|
注意:”< “ 与 “> “ 做字符串比较的时候,在 [ exp ] 情况下需要需要输入 “<” 或者 “>” 作为转义,或者使用 [[ > exp ]] 模式可以直接使用 “< “ 与 “> “ 作为判断符号使用。
1
2
3
4
5
6
| 比较两个字符串是否相等的办法是:
if [ "$test"x = "test"x ]; then
这里的关键有几点:
1 使用单个等号
2 注意到等号两边各有一个空格:这是unix shell的要求
3 注意到"$test"x最后的x,这是特意安排的,因为当$test为空的时候,上面的表达式就变成了x = testx,显然是不相等的。而如果没有这个x,表达式就会报错:[: =: unary operator expected
|
注意:==的功能在 [[]]
和 []
中的行为是不同的,如下:
1
2
3
4
5
| [[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么将为true
[[ $a == "z*" ]] # 如果$a等于z*(字符匹配),那么结果为true
[ $a == z* ] # File globbing 和word splitting将会发生
[ "$a" == "z*" ] # 如果$a等于z*(字符匹配),那么结果为true
|
数值判断
1
2
3
4
5
6
7
8
9
10
11
| -eq #equal 相等 如:if [ "$a" -eq "$b" ]
-ne #not equal 不相等 如:if [ "$a" -ne "$b" ]
-gt #greater than 大于 如:if [ "$a" -gt "$b" ]
-lt #less than 小于 if [ "$a" -lt "$b" ]
-ge #greater or equal 大于或等于 如:if [ "$a" -ge "$b" ]
-le #less or equal 小于或等于
< 小于(需要双括号),如:(("$a" < "$b"))
<= 小于等于(需要双括号),如:(("$a" <= "$b"))
> 大于(需要双括号),如:(("$a" > "$b"))
>= 大于等于(需要双括号),如:(("$a" >= "$b"))
|
例如:
1
2
3
4
5
6
7
| if [ 1 -eq 1 ]; then
echo "1=1?" # 输出结果
fi
if [ 2 -eq 1 ]; then
echo "2=1?" # 不输出结果
fi
|
-eq 和数字前后要有空格,数字和括号要有空格
逻辑判断
1
2
3
4
5
| [ ! EXPR ] 逻辑非,如果 EXPR 是false则返回为真。
[ EXPR1 -a EXPR2 ] 逻辑与,如果 EXPR1 and EXPR2 全真则返回为真。
[ EXPR1 -o EXPR2 ] 逻辑或,如果 EXPR1 或者 EXPR2 为真则返回为真。
[ ] || [ ] 用OR来合并两个条件
[ ] && [ ] 用AND来合并两个条件
|
其他判断
1
2
| [ -t FD ] 如果文件描述符 FD (默认值为1)打开且指向一个终端则返回为真
[ -o optionname ] 如果shell选项optionname开启则返回为真
|
判断一个变量是否为空
变量通过 " "
引号引起来
1
2
3
4
5
6
7
| #!/bin/sh
para1=
if [ ! -n "$para1" ]; then
echo "IS NULL"
else
echo "NOT NULL"
fi
|
【输出结果】”IS NULL”
直接通过变量判断
1
2
3
4
5
6
7
| #!/bin/sh
para1=
if [ ! $para1 ]; then
echo "IS NULL"
else
echo "NOT NULL"
fi
|
【输出结果】”IS NULL”
使用 test
判断
1
2
3
4
5
6
7
8
| #!/bin/sh
dmin=
if test -z "$dmin"
then
echo "dmin is not set!"
else
echo "dmin is set !"
fi
|
【输出结果】”dmin is not set!”
使用 ""
判断
1
2
3
4
5
6
7
8
| #!/bin/sh
dmin=
if [ "$dmin" = "" ]
then
echo "dmin is not set!"
else
echo "dmin is set !"
fi
|
【输出结果】”dmin is not set!”
if 高级特性
- 双圆括号
(( ))
:表示数学表达式
在判断命令中只允许在比较中进行简单的算术操作,而双圆括号提供更多的数学符号,而且在双圆括号里面的 ‘>’,’<’ 号不需要转意。 - 双方括号
[[ ]]
:表示高级字符串处理函数
双方括号中判断命令使用标准的字符串比较,还可以使用匹配模式,从而定义与字符串相匹配的正则表达式。
双括号的作用:在 shell 中,[ $ a != 1 || $b = 2 ]
是不允许出,要用 [ $ a != 1 ] || [ $b = 2 ]
,而双括号就可以解决这个问题的,[[ \$a != 1 || \$b = 2 ]]
。又比如这个 [ "$a" -lt "$b" ]
,也可以改成双括号的形式 ` ((“$a” < “$b”))`
案例
- 判断目录
$doiido
是否存在,若不存在,则新建一个
1
2
3
| if [ ! -d "$doiido"]; then
mkdir "$doiido"
fi
|
- 判断普通文件
$doiido
是否存,若不存在,则新建一个
1
2
3
| if [ ! -f "$doiido" ]; then
touch "$doiido"
fi
|
- 判断
$doiido
是否存在并且是否具有可执行权限
1
2
3
4
| if [ ! -x "$doiido"]; then
mkdir "$doiido"
chmod +x "$doiido"
fi
|
- 是判断变量 $doiido 是否有值
1
2
3
4
| if [ ! -n "$doiido" ]; then
echo "$doiido is empty"
exit 0
fi
|
- 两个变量判断是否相等
1
2
3
4
5
| if [ "$var1" = "$var2" ]; then
echo '$var1 eq $var2'
else
echo '$var1 not eq $var2'
fi
|
- 测试退出状态:
1
2
3
| if [ $? -eq 0 ];then
echo 'That is ok'
fi
|
- 数值的比较:
1
2
3
| if [ "$num" -gt "150" ]
echo "$num is biger than 150"
fi
|
a>b 且 a<c
1
2
3
| (( a > b )) && (( a < c ))
[[ $a > $b ]] && [[ $a < $c ]]
[ $a -gt $b -a $a -lt $c ]
|
a>b 或 a<c
1
2
3
| (( a > b )) || (( a < c ))
[[ $a > $b ]] || [[ $a < $c ]]
[ $a -gt $b -o $a -lt $c ]
|
- 检测执行脚本的用户
1
2
3
4
5
6
| if [ "$(whoami)" != 'root' ]; then
echo "You have no permission to run $0 as non-root user."
exit 1;
fi
# 上面的语句也可以使用以下的精简语句
[ "$(whoami)" != 'root' ] && ( echo "You have no permission to run $0 as non-root user."; exit 1 )
|
- 正则表达式
1
2
3
4
| doiido="hero"
if [[ "$doiido" == h* ]];then
echo "hello,hero"
fi
|
- 查看当前操作系统类型
1
2
3
4
5
6
7
8
9
10
11
| #!/bin/sh
SYSTEM=`uname -s`
if [ $SYSTEM = "Linux" ] ; then
echo "Linux"
elif [ $SYSTEM = "FreeBSD" ] ; then
echo "FreeBSD"
elif [ $SYSTEM = "Solaris" ] ; then
echo "Solaris"
else
echo "What?"
fi
|
- if 利用 read 传参判断
1
2
3
4
5
6
7
8
9
10
11
12
13
| #!/bin/bash
read -p "please input a score:" score
echo -e "your score [$score] is judging by sys now"
if [ "$score" -ge "0" ]&&[ "$score" -lt "60" ];then
echo "sorry,you are lost!"
elif [ "$score" -ge "60" ]&&[ "$score" -lt "85" ];then
echo "just soso!"
elif [ "$score" -le "100" ]&&[ "$score" -ge "85" ];then
echo "good job!"
else
echo "input score is wrong , the range is [0-100]!"
fi
|
- 判断文件是否存在
1
2
3
4
5
6
7
8
9
10
| #!/bin/sh
today=`date -d yesterday +%y%m%d`
file="apache_$today.tar.gz"
cd /home/chenshuo/shell
if [ -f "$file" ];then
echo "OK"
else
echo "error $file" >error.log
mail -s "fail backup from test" loveyasxn924@126.com <error.log
fi
|
- 这个脚本在每个星期天由 cron 来执行。如果星期的数是偶数,他就提醒你把垃圾箱清理
1
2
3
4
5
6
| #!/bin/bash
WEEKOFFSET=$[ $(date +"%V") % 2 ]
if [ $WEEKOFFSET -eq "0" ]; then
echo "Sunday evening, put out the garbage cans." | mail -s "Garbage cans out" your@your_domain.org
fi
|
切换目录 cd pushd popd dirs
cd
- 普通目录切换:
cd xxx
- 两个目录之间切换:
cd -
- 作用是可以回到前一个目录,只支持最近操作的两个目录
pushd、popd 和 dirs 多个目录切换
- pushd:切换到作为参数的目录,并把原目录和当前目录压入到一个虚拟的堆栈中;如果不指定参数,则会回到前一个目录,并把堆栈中最近的两个目录作交换
- popd: 弹出堆栈中最近的目录
- dirs:列出当前堆栈中保存的目录列表
- -p 参数可以每行一个目录的形式显示堆栈中的目录列表
- -v 参数可以在目录前加上编号 注意: 有 -v 时,不添加 -p 也可以每行一个目录的形式显示
- 最近压入堆栈的目录位于最上面
示例一:pushd 命令切换目录
1
2
3
4
5
6
7
8
9
10
11
12
13
| ❯ dirs -p -v
0 ~
❯ pushd /usr/local/bin
/usr/local/bin ~
❯ dirs -p -v
0 /usr/local/bin
1 ~
❯ pushd /usr/share/man
/usr/share/man /usr/local/bin ~
❯ dirs -p -v
0 /usr/share/man
1 /usr/local/bin
2 ~
|
示例二:最近的两个目录之间切换
在最近的两个目录之间切换:用 pushd 不加参数即可
1
2
3
4
5
6
7
8
| ❯ dirs -p -v
0 /usr/share/man
1 /usr/local/bin
2 ~
❯ pushd
/usr/local/bin /usr/share/man ~
❯ pushd
/usr/share/man /usr/local/bin ~
|
也可以用 cd -
示例三:如何在多个目录之间切换?
pushd +n
n 是一个数字,有此参数时,是切换到堆栈中的第 n 个目录,并把此目录以堆栈循环的方式推到堆栈的顶部(需要注意: 堆栈从第 0 个开始数起)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| ❯ dirs -p -v
0 /cores
1 ~
2 /usr/share/man
3 /usr/local/bin
4 /usr
❯ pushd +2
/usr/share/man /usr/local/bin /usr /cores ~
❯ dirs -p -v
0 /usr/share/man
1 /usr/local/bin
2 /usr
3 /cores
4 ~
❯ pwd
/usr/share/man
❯ pushd +2
/usr /cores ~ /usr/share/man /usr/local/bin
|
示例四:如何把目录从堆栈中删除?
用 popd 即可
- popd 不加参数:popd 把堆栈顶端的目录从堆栈中删除,并切换于位于新的顶端的目录
- popd 加有参数 +n 时,n 是堆栈中的第 n 个目录,表示把堆栈中第 n 个目录从堆栈中删除
- 位于堆栈顶部的目录是当前目录,它不能被 pop 出去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # popd不加参数,默认删除栈顶,并切换目录到新的栈顶目录
❯ pwd
/usr
❯ dirs -p -v
0 /usr
1 /cores
2 ~
3 /usr/share/man
4 /usr/local/bin
❯ popd
/cores ~ /usr/share/man /usr/local/bin
# popd +n 删除栈中n位置的目录
❯ dirs -p -v
0 /system
1 /cores
2 ~
3 /usr/share/man
4 /usr/local/bin
❯ popd +2
/system /cores /usr/share/man /usr/local/bin
❯ dirs -p -v
0 /system
1 /cores
2 /usr/share/man
3 /usr/local/bin
|
获取用户输入 read
在 shell 脚本中,使用 read
命令获取命令行输入
read 命令参数
- -p(指定提示语句)
- -n(限定字符个数)
- -t(设置等待时间)
- -s(不显示)
基本读取
read 命令接收标准输入(键盘)的输入,或者其他文件描述符的输入。在得到输入之后,read 命令把输入数据放入一个标准变量中
1
2
3
4
5
6
7
8
9
10
| #!/bin/zsh
/bin/echo -n "Enter your name:" # 参数-n的作用是不换行,echo默认换行;在Mac上-n不起作用,需要使用/bin/echo
read name # 把键盘输入放入变量name
echo "hello $name, welcome to my program" # 显示输入信息
exit 0 # 返回一个零退出状态,退出shell程序
# 输出结果
# ❯ sh test.sh
# Enter your name:hacket
# hello hacket, welcome to my program
|
也可以用 -p
简化上面的写法,它允许在 read 命令行中直接指定一个提示语句:
1
2
3
| #!/bin/zsh
read -p "Enter your name:" name #提示用户输入
echo "hello $name, welcome to my program" #输出结果
|
上例中,read 后面的变量只有 name 一个,实际上可以有多个。如果有多个输入数据,则应指定多个变量,这样第一个数据赋给第一个变量,第二个数据赋给第二个变量…
1
2
3
4
5
6
7
8
9
10
| read -p "Enter your,name,age,id_card,address:" name age id_card address # 通过参数-p指定一个提示语句,并把键盘输入放入变量name
echo "hello -$name =$age ==$id_card -=$address , welcome to my program"
# 输出结果
❯ sh test.sh # 输入参数以空格区分
Enter your,name,age,id_card,address:1 2 3 4
hello -1 =2 ==3 -=4 , welcome to my program
❯ sh test.sh # 输入的参数如果是逗号,那就不对了
Enter your,name,age,id_card,address:1,2,3,4
hello -,2,3,4 = == -= , welcome to my program
|
如果输入数据个数过多,远大于变量个数,则多余的所有数据都给最后一个变量;如果输入数据太少,不会结束(mac 上表现是少了的参数为默认空值)。
1
2
3
4
5
6
7
8
9
10
| ## -p 指定多个变量
read -p "Enter your,name,age,id_card,address:" name age id_card address # 通过参数-p指定一个提示语句,并把键盘输入放入变量name
echo "hello -$name =$age ==$id_card -=$address , welcome to my program"
❯ sh test.sh # 输入参数过多,多的都给最后一个变量
Enter your,name,age,id_card,address:1 2 3 4 5 6 7
hello -1 =2 ==3 -=4 5 6 7 , welcome to my program
# mac参数少了,少的参数就是默认值
Enter your,name,age,id_card,address:1 2 3
hello -1 =2 ==3 -= , welcome to my program
|
如果不指定变量名,那么 read 命令把接收到的输入放在环境变量 REPLY
中,例如 read -p “Enter a number”。环境变量 REPLY 中包含输入的所有数据,在 shell 脚本中,我们可以正常使用环境变量 REPLY。
1
2
| read -p "Enter a number: "
echo $REPLY
|
输入计时,输入计数
计时
使用 read 命令有潜在危险,脚本很可能停下来一直等待用户输入。使用 -t 选项指定一个计时器,设置等待输入的秒数。当计时器满时,read 命令返回一个非零退出状态。
1
2
3
4
5
6
| if read -t 5 -p "please enter your name:" name # -t,设置输入超时时间(本语句设置超时时间为5秒),默认单位是秒;-p,指定输入提示
then # 如果不超过5秒
echo "hello $name ,welcome to my script"
else # 超过5秒
echo "Timeout"
fi
|
计数
read 命令除了可以设置输入计时,还可以设置输入计数。当输入的字符数目达到设定数目时,自动退出,并把输入数据赋给变量
1
2
3
4
5
6
7
8
9
10
11
| # !/bin/bash # 指定shell类型
read -n2 -p "Do you want to continue [Y/N]?" answer
case $answer in
(Y | y)
echo "fine, continue";;
(N | n)
echo "ok, good bye";;
(*)
echo "error choice";;
esac
|
该例子使用了 -n
选项,后接数值 1,只要按下 1 个字符进行回答,无需按回车键,read 命令立即接受输入并将其传给变量;如果输入的字符个数小于 -n 选项数值,我们就需要按回车键。
默读(输入不显示在监视器上)
有时,脚本需要用户输入,但用户不希望输入数据显示在监视器上。典型的例子就是输入密码。-s
选项能够使 read 命令中的输入数据不显示在监视器上(实际上,数据是显示的,只是 read 命令把文本颜色设置成与背景相同)
1
2
3
4
5
6
7
| # !/bin/bash # 指定shell类型
# 默读(输入不显示在监视器上,如读取隐藏数据、用户密码)
read -s -p "Enter your password:" passwd
echo # read默认不换行,echo用于换行
echo "your password is $passwd"
|
sed 修改文件
sed 命令
sed 全名为 stream editor,流编辑器,用程序的方式来编辑文本,功能相当的强大。
Mac sed
案例
修改一个 txt 文本信息
1
2
3
4
5
6
7
8
9
10
11
| // 文件test.txt内容:
12345
aaa
CCC
// 执行命令
sed -i "" "s/12345/Hello/" test.txt
// 文件test.txt内容:
Hello
aaa
CCC
|
- 分析命令
sed -i "" "s/12345/Hello/" test.txt
- -i 直接操作文件并不需要备份文件,如果需要备分则使用 -i “benfen”
- s 代表 substitute 即替换
- 12345 要被代替的文字
- Hello 要代替的文字
- test.txt 对应修改的文件
修改 demo.properties
1
2
3
| user.name=test
user.password=123456
gioenable=false
|
- 将 user.name 和 user.password 的 value 值替换为实际需要的用户名和密码
1
2
| sed -i "" "s#^user.name=.*#user.name=hacket#g" demo.properties
sed -i "s#^user.password=.*#user.password=密码#g" demo.properties
|
- 将 gioenable=false 改为 gioenable=true
1
2
3
| sed -i "" '/gioenable/s/false/true/g' demo.properties
# 或
sed -i "" "s#^gioenable=.*#gioenable=true#g" demo.properties
|
修改 gradle.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| sed -i "" '/libcommon/s/false/true/g' gradle.properties
sed -n '/^libcommon.*/p' gradle.properties
sed -i "" '/libwidget/s/false/true/g' gradle.properties
sed -n '/^libwidget.*/p' gradle.properties
sed -i "" '/core/s/false/true/g' gradle.properties
sed -n '/^core.*/p' gradle.properties
./gradlew :libcommon:publishLibCommonPublicationToHacketGitHubPackagesRepository
./gradlew :libwidget:publishLibWidgetPublicationToHacketGitHubPackagesRepository
./gradlew :core:publishReleasePublicationToHacketGitHubPackagesRepository
sed -i "" '/libcommon/s/true/false/g' gradle.properties
sed -n '/^libcommon.*/p' gradle.properties
sed -i "" '/libwidget/s/true/false/g' gradle.properties
sed -n '/^libwidget.*/p' gradle.properties
sed -i "" '/core/s/true/false/g' gradle.properties
sed -n '/^core.*/p' gradle.properties
|
sed 功能
修改文件要用 -i, 另外基本操作码如下,以及熟悉简单的正则表达式就 OK
1
2
3
4
5
| s 替换
c change line
d 删除line
i 插入line
a 追加line
|
d 删除
1
| sed -i "" "2d" test.txt #删除第二行
|
删除第二行,此时 test.txt 内容:
1
| sed -i "" "2d" test.txt # 2代表第二行
|
1
| sed -i "" "/^C.*/d" test.txt # C.* 代表以C开头的行数,.*是通配符
|
1
| sed -i "" "/2/d" test.txt # 2 代表包含 2 的行
|
1
| sed -i "" "/^$/d" test.txt
|
p 查
demo.properties 内容:
1
2
3
| user.name=test
user.password=123456
gioenable=true
|
1
| sed -n "p" demo.properties
|
输出:
1
2
3
| user.name=test
user.password=123456
gioenable=true
|
1
| sed -n "2p" demo.properties
|
输出:
1
| sed -n '/^gioenable.*/p' demo.properties
|
输出:
1
| sed -n '/^user.*/p' demo.properties
|
输出:
1
2
| user.name=test
user.password=123456
|
s 改
1
| sed -i "" "s/Hello/xxxx/" test.txt # Hello 表示要修改的文字,xxx 表示替代的
|
1
| sed -i "" "4s/xxxx/aaa/" test.txt # 4 代表第四行
|
a 新增
1
2
3
4
5
| sed -i "" '2 a\
apply from: \"\$rootDir\/config\/gradle\/config_bytex.gradle\" \
apply from: \"\$rootDir\/config\/gradle\/config_bytex_method_call_opt.gradle\" \
apply from: \"\$rootDir\/config\/gradle\/config_bytex_third_part_fix.gradle\" \
' demo.properties
|
原内容:
1
2
3
| user.name=test
user.password=123456
gioenable=true
|
修改后:
1
2
3
4
5
6
| user.name=test
user.password=123456
apply from: "$rootDir/config/gradle/config_bytex.gradle"
apply from: "$rootDir/config/gradle/config_bytex_method_call_opt.gradle"
apply from: "$rootDir/config/gradle/config_bytex_third_part_fix.gradle"
gioenable=true
|
其他命令
which 查看命令所在路径
用来查看当前要执行的命令所在的路径。
which 命令的原理:在 PATH 变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用 which 命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。
whereis
用来查看一个命令或者文件所在的路径。
whereis 命令原理:只能用于程序名的搜索,而且只搜索二进制文件(参数 -b)、man 说明文件(参数 -m)和源代码文件(参数 -s)。如果省略参数,则返回所有信息。
awk
AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。
之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符
语法
1
2
3
| awk [选项参数] 'script' var=value file(s)
或
awk [选项参数] -f scriptfile var=value file(s)
|
- -F fs or –field-separator fs
指定输入文件折分隔符,fs 是一个字符串或者是一个正则表达式,如 -F:。 - -v var=value or –asign var=value
赋值一个用户定义变量。 - -f scripfile or –file scriptfile
从脚本文件中读取 awk 命令。
用法
log.txt 文本内容如下:
2 this is a test 3 Do you like awk This’s a test 10 There are orange,apple,mongo
用法 1:awk '{[pattern] action}' {filenames} # 行匹配语句 awk '' 只能用单引号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 每行按空格或TAB分割,输出文本中的1、4项
$ awk '{print $1,$4}' log.txt
# ---------------------------------------------
#2 a
#3 like
#This's
#10 orange,apple,mongo
# 格式化输出
$ awk '{printf "%-8s %-10s\n",$1,$4}' log.txt
# ---------------------------------------------
#2 a
#3 like
#This's
#10 orange,apple,mongo
|
用法 2:配合 grep 管道来打印 APP 的进程
1
2
3
4
5
6
| ps -e | grep ClashX.app | awk 'NR==1{print $1}'
# 或
ps -ef | grep $1 | awk 'NR==1{print $2}'
# kill进程
ps -ef | grep $1 | awk 'NR==1{print $2}' | xargs kill -9
|
- awk 后 $ 1 表示进程信息的第一列,即第一个元素。 获取 PID 需先确定 PID 位于进程信息的列数,即通过
$N
获取 PID。 NR==1
表示取第一行数据