已"#" 作为注释, 除了 以"#!" 开头的。
"#!/bin/bash" 会告诉shell用哪个shell来运行脚本(必须要再第一行)
文本字符串和命令输出显示在同一行
shell文件
#!/bin/bash
echo -n "The time and date are: "
date
打印 :The time and date are:Tue Apr 2 10:28:38 CST 2019
在shell脚本中,调用命令
shell文件
testParm1 = $(date)
testParm2 = `date` //反引号,‘~’那个键
用例:
#!/bin/bash
today=$(date +%y%m%d) //%y%m%d --> 190402 +%Y%m%d --> 20190402
ls /usr/bin -al > log.$today
Shell 传递参数
我们可以在执行 Shell 脚本时,向脚本传递参数
#!/bin/bash
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
参数处理 | 说明 |
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
重定向
输出重定向
可以将命令的输出重定向到另一个位置(比如文件)
-
'>' '>>' '>' 例如输出到文件,会执行覆盖操作; '>>' 输出到文件会追加到文件末尾。
输入重定向
环境变量
env printenv
显示变量
打印 echo $【变量名】 例:echo $JAVA_HOME
直接显示环境变量,对应的文件夹内容
[newcs@dsszbyz-etl-inf7 day]$ ll $JAVA_HOME
total 25928
drwxr-xr-x 2 root root 4096 Mar 16 2018 bin
-rw-r--r-- 1 root root 3244 Dec 20 2017 COPYRIGHT
drwxr-xr-x 4 root root 147 Mar 16 2018 db
drwxr-xr-x 3 root root 4096 Mar 16 2018 include
-rw-r--r-- 1 root root 5202881 Dec 20 2017 javafx-src.zip
drwxr-xr-x 5 root root 4096 Mar 16 2018 jre
drwxr-xr-x 5 root root 4096 Mar 16 2018 lib
-rw-r--r-- 1 root root 40 Dec 20 2017 LICENSE
drwxr-xr-x 4 root root 60 Mar 16 2018 man
-rw-r--r-- 1 root root 159 Dec 20 2017 README.html
-rw-r--r-- 1 root root 424 Dec 20 2017 release
-rw-r--r-- 1 root root 21095860 Dec 20 2017 src.zip
-rw-r--r-- 1 root root 63933 Dec 20 2017 THIRDPARTYLICENSEREADME-JAVAFX.txt
-rw-r--r-- 1 root root 145180 Dec 20 2017 THIRDPARTYLICENSEREADME.txt
设置局部用户定义变量
[newcs@dsszbyz-etl-inf7 ~]$ myParm="parm1" //定义变量
[newcs@dsszbyz-etl-inf7 ~]$ echo $myParm
parm1
[newcs@dsszbyz-etl-inf7 ~]$ unset myParm //删除变量
[newcs@dsszbyz-etl-inf7 ~]$ echo $myParm
[newcs@dsszbyz-etl-inf7 ~]$
定义数组
mytest=("one" "two" "three" "four" "five")
${mytest[1]} //two
//打印数组所有值
echo ${mytest[*]}
parm1=("day_id" "${v_dayid}")
parm2=("hour_id" "${v_hourid}")
parmlist=(${parm1} ${parm2})
追加环境变量 PATH
环境变量的本质是方便运行程序命令。
例如,运行java。没有配置PATH时会报找不到的错误,必须要使用全路径才能找到。
多个路径是用“:”分割,追加在末尾时。
PATH = $PATH:【路径】
PATH变量的修改只能持续到退出或重启系统
环境变量持久化
/etc/profile.d目录中创建一个以.sh结尾的文件。把所有新的或修改过的全局环境变
量设置放在这个文件中。
在大多数发行版中,存储个人用户永久性bash shell变量的地方是$HOME/.bashrc文件。这一
点适用于所有类型的shell进程。
但如果设置了BASH_ENV变量,那么记住,除非它指向的是$HOME/.bashrc,否则你应该将非交互式shell的用户变量放在别的地方
命令替换(常用与将命令输出赋给变量)
命令替换会创建一个子shell来运行对应的命令
-
`命令` 反引号字符(`)
-
$(命令) $()格式
数学运算
1.expr 命令,使用诸多限制
2.使用方括号 "$[数学表达式]"
-
bash shell数学运算但只支持整数运算
-
z shell 提供了完整的浮点数算术操作
3.bash计算器(bc)
[newcs@dsszbyz-etl-inf7 ~]$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
3.5 / 3
1
scale=2
3.5 / 3
1.16
scale 默认 0 ,代表显示的小数位
使用变量
[newcs@dsszbyz-etl-inf7 ~]$ bc -q
var1 = 1
var2 = var1*0.123456
print var2
print var2
.123456
scale=3
print var2
.123456
var2 *100
12.345600
在shell脚本中,使用bc计算器
#!/bin/bash
var1=100
var2=45
var3=$(echo "scale=4; $var1 / $var2" | bc)
//var3=`echo "scale=4; $var1 / $var2" | bc` 命令替换
退出状态码
变量$?来保存上个已执行命令的退出状态,它的值会变成由shell所执行的最后一条命令
的退出状态码。
成功:0
结束时错误: 一个整数值
[newcs@dsszbyz-etl-inf7 ~]$ echo $?
0
[newcs@dsszbyz-etl-inf7 ~]$ dsf
-bash: dsf: command not found
[newcs@dsszbyz-etl-inf7 ~]$ echo $?
127
常见Linux退出状态码
状态码 | 描 述 |
0 | 命令成功结束 |
1 | 一般性未知错误 |
2 | 不适合的shell命令 |
126 | 命令不可执行 |
127 | 没找到命令 |
128 | 无效的退出参数 |
128+x | 与Linux信号x相关的严重错误 |
130 | 通过Ctrl+C终止的命令 |
255 | 正常范围之外的退出状态码 |
退出状态码126表明用户没有执行命令的正确权限。
结构化编程
1.
if 【command】
then
【command】
fi
//格式2,和上相同
if 【command】;then
【command】
fi
2.
if 【command】;then
【commands】
else
【commands】
fi
3.
if【command】
then
【commands】
elif 【command】; then
【commands】
fi
判断命令的退出状态码为0 就执行 then中的内容
test 命令
条件成立,返回0
格式一:
if test 【条件】 ;then
【commands】
else
【commands】
fi
格式二:
if [条件] ;then
【commands】
else
【commands】
fi
如果test 里的条件是变量,不为空返回0 ,
test支持可以三类判断条件:
数值比较 (只能比较整数,小数会报错)
字符串比较
文件比较
数值比较
-eq | = | 相等 |
-ge | >= | 大于或等于 |
-gt | > | 大于 |
-le | <= | 小于或等于 |
-lt | < | 小于 |
-ne | != | 不等于 |
字符串比较
= | str1 = str2 | 检查str1是否和str2相同 |
!= | str1 != str2 | 检查str1是否和str2不同 |
< | str1 < str2 | 检查str1是否比str2小 |
> | str1 > str2 | 检查str1是否比str2大 |
-n | -n str1 | 检查str1的长度是否非0 |
-z | -z str1 | 检查str1的长度是否为0 |
使用"<"、">" 时,要转义,不然容易被程序识别为重定向
文件比较
-d file | 检查file是否存在并是一个目录 |
-e file | 检查file是否存在 |
-f file | 检查file是否存在并是一个文件 |
-r file | 检查file是否存在并可读 |
-s file | 检查file是否存在并非空 |
-w file | 检查file是否存在并可写 |
-x file | 检查file是否存在并可执行 |
-O file | 检查file是否存在并属当前用户所有 |
-G file | 检查file是否存在并且默认组与当前用户相同 |
file1 -nt file2 | 检查file1是否比file2新 |
file1 -ot file2 | 检查file1是否比file2旧 |
并、或运算
if [ 条件1] && [ 条件2]
双括号命令
if((操作))
可包括test命令的(但使用 ">" 时不用转义),还有
val++ | 后增 |
val-- | 后减 |
++val | 先增 |
--val | 先减 |
! | 逻辑求反 |
~ | 位求反 |
** | 幂运算 |
<< | 左位移 |
>> | 右位移 |
& | 位布尔和 |
| | 位布尔或 |
&& | 逻辑和 |
|| | 逻辑或 |
for循环
for 【变量名】 in 【变量组】 ;do
【commands】
done
遍历目录
for file in /home/rich/test/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
fi
done
ps 将$file变量用双引号圈起来。如果不这么做,遇到含有空格的目录名或文件名时就会有错误产生。
c风格的for
#!/bin/bash
for (( i=1; i <= 10; i++ ))
do
echo "The next number is $i"
done
$ ./test8
The next number is 1
The next number is 2
The next number is 3
for (( a=1, b=10; a <= 10; a++, b-- ))
until 直到
只有测试命令的退出状态码不为0,bash shell才会执行循环中列出的命令。一旦测试命令返回了退出状态码0,循环就结束。
break结束 后面可以跟参数 break 2 终止出第二层循环
处理循环的输出
for file in /home/rich/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif
echo "$file is a file"
fi
done > output.txt
//done | sort
处理用户参数
命令行参数
参数会默认以空格区分,传字符串需用 单引号或双引号
校验命令行参数
使用命令行参数时,一定要检查,否则容易报错。
校验方法1
if [ -n "$1" ]
then
echo Hello $1, glad to meet you.
else
echo "Sorry, you did not identify yourself. "
fi
校验方法2
直接对比输入命令行参数个数
if [$# -ne 3];then
//参数个数不等于3
else
---------------------------
if [$# -eq 3];then
//参数个数等于3
else
---------------------------
if (( $# == 3)) ;then
echo "参数= 3"
else
echo "参数!= 3"
fi
获取最后一个命令行参数
${!#} 而不是 ${$#} //暂时这种方法无效
params=$#
$params
获取所有参数
$* 将参数视为一个整体,而不是多个个体
$@ 将参数当作同一字符串中的多个独立的单词
处理多个参数(日常函数的 处理方式)
getopt、getopts
获取用户输入(非命令行方式)
#!/bin/bash
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my program. "
#方法二:
read -p "Please enter your age: " age
days=$[ $age * 365 ]
echo "That makes you over $days days old! "
#!/bin/bash
echo -n "your name?"
read name
echo "you are $name"
read -p "your age" age
echo "your are $age years old"
echo -n // 打印后,不换行
read
-
-p 指定单个或多个参数
-
-t 限定超时时长(秒) 超时未输入,返回非零的状态码
-
-n1
-
-s 不显示输入信息,比如密码(实际上只是将文本颜色设成跟背景色一样)
-
-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
-
-d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
-
-p 后面跟提示信息,即在输入前打印提示信息。
-
-e 在输入的时候可以使用命令补全功能。
-
-n 后跟一个数字,定义输入文本的长度,很实用。
-
-r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了。
-
-s 安静模式,在输入字符时不再屏幕上显示,例如login时输入密码。
-
-t 后面跟秒数,定义输入字符的等待时间。
-
-u 后面跟fd,从文件描述符中读入,该文件描述符可以是exec新开启的。
从文件中读取(每次读一行)
对文件使用cat命令,将结果通过管道直接传给含有read命令的while命令
#!/bin/bash
conut=1
cat file.conf | while read line2
do
echo "line #$count value $line2"
((count++))
done
ps:默认是以行分割,因为read 命令是换行读取
使用重定向输入读取文件
#!/bin/bash
# 重定向输入,读取文件
exec 0< file.conf
count=1
while read line
do
echo "Line #$count: $line"
count=$[ $count + 1 ]
done
重定向错误
错误的文件描述符 值是 2
Linux的标准文件描述符 | ||
文件描述符 | 缩写 | 描 述 |
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误 |
只重定向错误
#!/bin/baash
#将错误信息重定向到 文件
cat new-6.sh 2> erro.log
echo "test command ehco" 2>> erro.log
[newcs@dsszbyz-etl-inf7 shell_test_dir]$ cat erro.log
cat: new-6.sh: No such file or directory
同时将重定向错误和正常输出
#!/bin/baash
ls shell_test_dir bad 2>>shell_test_dir/erro.log 1>>shell_test_dir/info.log
ls shell_test_dir bad &>>shell_test_dir/debug.log
[newcs@dsszbyz-etl-inf7 shell_test_dir]$ cat debug.log
ls: cannot access bad: No such file or directory
shell_test_dir:
debug.log
erro.log
file.conf
info.log
new-2.sh
new-3.sh
new-4.sh
new-5.sh
"&>>" 、"&>" 将正确错误都重定向到同一个位置
永久重定向
exec 1> file
#!/bin/bash
#永久重定向
#exec 1>>info.log
exec &>>debug.log
echo `date "+%Y-%m-%d %H:%M:%S"`
#exec 2>> error.log
ll ../shell_test_dir ../bad2
空文件/dev/null
-
不显示存储输出 ls -al badfile test16 2> /dev/null
-
删除文件内容 cat /dev/null > error.log
创建临时文件,并保存文件路劲、文件名
#!bin/bash
#创建临时文件
fileName=`mktemp test.XXX` //mktemp命令会生成随机3个字符代替X(必须大写)
echo "temp file : `pwd`/$fileName " //会建在当前目录下
#强制建在/tmp 中
fileName=`mktemp -t test.XXX`
echo "temp file : $fileName " // /tmp/test.wjy
同时重定向到文件和显示器
#!/bin/bash
#tee 命令 相当于T型管道,同时定向到文件和显示器
echo "test tee" | tee -a info.log
cat info.log | tee -a info.log
-
-a 追加内容到文件末尾 , 没有 a 则执行覆盖操作
例:
读取csv文件,生成sql文件
#!bin/bash
# 读取csv文件,生成sql文件
outfile='demo1.sql'
IFE=','
while read param1 param2 param3
do
echo "insert into table values ($param1,$param2,$param3)" >> $outfile
#done < "file.csv"
done <$1
执行字符串中的命令
#!/bin/bash
#清理日志
command="cat /dev/null > error.log"
#command="ls"
echo "$command"
eval $command
常用时间格式化
date "+%Y-%m-%d %H:%M:%S"
控制shell
-
Ctrl+Z 停止当前shell中的任何进程,不是终止
Linux信号 | ||
信号 | 值 | 描 述 |
0 | EXIT | 结束退出 |
1 | SIGHUP | 挂起进程 |
2 | SIGINT | 终止进程 |
3 | SIGQUIT | 停止进程 |
9 | SIGKILL | 无条件终止进程 |
15 | SIGTERM | 尽可能终止进程 |
17 | SIGSTOP | 无条件停止进程,但不是终止进程 |
18 | SIGTSTP | 停止或暂停进程,但不终止进程 |
19 | SIGCONT | 继续运行停止的进程 |
命令trap 捕捉对应的信号,将其截断,交由本地处理,而不是shell
例:
trap命令来忽略SIGINT(终止)信号
#!/bin/bash
# 捕捉trap,不让终止操作
echo "---start"
trap " echo 'can not stop'" 2 //将命令放到字符串中
count=0
for (( ; count<10; count++))
do
echo " #$count"
sleep 1
done
echo "---end"
trap "echo Goodbye..." 0 //shell 结束退出
[newcs@dsszbyz-etl-inf7 shell_test_dir]$ sh demo2.sh
---start
#0
#1
#2
#3
#4
^Ccan not stop
#5
^Ccan not stop
#6
^Ccan not stop
#7
#8
#9
---end
trap 的修改和移除
-
修改 在需要修改的地方,新增就成
-
移除 在需要移除的地方,加 trap -- 【状态】 trap -- 2
后台运行
&
在命令后加 "&" sh test.sh & 但会将输出和错误信息都打印出来,和操作混合了,最好将脚本的STDOUT和STDERR重定向,同时退出终端时,会终止。
nohup
阻断所有发送给该进程的SIGHUP信号。即可在退出终端会话时阻止进程退出
自动将STDOUT和STDERR的消息重定向到一个名为nohup.out的文件中
指定nohup 输出
[newcs@dsszbyz-etl-inf7 shell_test_dir]$ nohup sh demo2.sh 1>info.log 2>error.log &
作业控制:启动、停止、终止以及恢复作业的这些功能
$$ 当前脚本的PID 进程号
优先级
nice -n 10 ./test4.sh nice -n 【级别】 【命令】
renice -n 10 -p 5055 renice -n 【级别】 -p 【PID】
函数
#!/bin/bash
格式一
function1() {
echo "trying to display a non-existent file"
ls -l badfile
# return 6 设置返回状态码(只能0~255)
}
格式二
function 【方法名】{
【command】
}
echo "testing the function: "
function1 //调用函数
echo "The exit status is: $?" //使用函数的结束状态码
让函数输出
#!/bin/bash
# 函数输出
f1(){
read -p "your age: " age
echo $age
}
age=`f1`
echo "you are $age old"
[newcs@dsszbyz-etl-inf7 shell_test_dir]$ sh 2-new.sh
your age: 2
you are 2 old
使用打印的方式,将函数值返回。
#!/bin/bash
# 函数输出
f2(){
param=("array1" "array2")
echo ${param[*]}
}
array=`f2`
echo "array : $array" //输出array : array1 array2
echo " ${arry[*]} | ${array[0]} | ${array[1]} " //输出 | array1 array2 |
此种方法返回的是字符串,而不是数组
给函数传参数
#!bin/bash/
# 给函数传参数
f1(){
#echo $[ $1 - $2 ] //参数只属于函数,和脚本的参数无直接关系
}
val=`f1 $1 $2` //必须传命令行参数,否则会报错
echo "resualt= $val"
函数相当于小的shell脚本,可接受命令行参数,对参数的检验方法与脚本一致
在脚本主体运行函数时,没有使用命令替换(没有创建子shell),也可以。但无法传参数,无法返回值给变量。虽然脚本的任何参数都是全局,但得不偿失。
f1(){
echo "test"
}
#val=f1 $1 $2 //无法传值
f1
[newcs@dsszbyz-etl-inf7 shell_test_dir]$ sh 3-new.sh
test
函数接收、返回数组
#!/bin/bash/
# 函数接收、返回数组
f1(){
array1=(`echo $@`)
#l=" funci array1 : ${array[0]} | ${array[1]} | ${array[2]}"
#`echo $l`
array2=(1 2 3 4 5)
echo ${array2[*]}
}
array=(3 2 1)
array2=`f1 ${array[*]}`
echo "1_shell array2 : $array2" //返回的是字符串
array3=(`echo ${array2[*]}`) //符合数组的构造方法
echo "1_shell array : ${array[*]}"
echo "1_shell array3 : ${array3[0]} | ${array3[1]} | ${array3[2]}"
[newcs@dsszbyz-etl-inf7 shell_test_dir]$ sh 5-new.sh
1_shell array2 : 1 2 3 4 5
1_shell array : 3 2 1
1_shell array3 : 1 | 2 | 3