一劳永逸
给shell脚本赋予执行权限,然后直接执行
[root@cuizja learnlinux]# chmod +rwx readtest.sh
[root@cuizja learnlinux]# ./readtest.sh
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]#
动态选择
每次执行动态的赋予脚本类型
[root@cuizja learnlinux]# /bin/bash readtest.sh
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]#
通过bash
命令运行,bash
命令中指定了bash的路径
[root@cuizja learnlinux]# bash readtest.sh
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]#
[root@cuizja learnlinux]# source readtest.sh
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]#
. filenname
[root@cuizja learnlinux]# . readtest.sh
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]#
变量定义的三种方式
变量名=值
变量名='值'
变量名="值"
**注意:**这里的赋值号
=
两侧不能有空格如果值中没有空白字符(空格,tab缩进等),就可以不用引号
变量命名规范
变量的调用
变量使用$
符号调用,但是有的时候需要和字符串共同输出是可以加{}
区分变量和字符串的边界,例如
name='ccc'
echo "I am ${name}ccc"
这里通过{}
区分调用的是name
变量而不是nameccc
.
这里推荐调用变量的时候加上
{}
,这是个良好的编程习惯
变量的修改
变量的修改和变量的定义是相同的语句,不必在变量前加
$
单引号和双引号的区别
单引号中变量进行原样输出,即使遇到
${}
也不会解析成变量调用语句双引号中的变量会对变量中的调用进行解析,然后输出
[root@cuizja learnlinux]# vim doubletest.sh
#!/bin/bash
name=abc
echo 'name=${name}'
echo "name=${name}"
[root@cuizja learnlinux]# ./doubletest.sh
name=${name}
name=abc
建议数字不加引号,如果必须原样输出就加单引号,否则一律添加双引号
将命令结果赋值给变量
shell也支持将命令的执行结果赋值给变量,常见有以下两种写法
这两种方式能够达到相同的效果,但是一般推荐使用
$()
,因为在编辑器中反引号容易被人看错成单引号
[root@cuizja learnlinux]# vim testfanyinhao.sh
#!/bin/bash
echo $(ls /usr/learnlinux)
echo `ls /usr/learnlinux`
[root@cuizja learnlinux]# ./testfanyinhao.sh
doubletest.sh hello.sh helloworld.sh readtest.sh testfanyinhao.sh test.log
doubletest.sh hello.sh helloworld.sh readtest.sh testfanyinhao.sh test.log
只读变量
使用readonly
命令可以将变量定义为只读变量,只读变量不能被修改
删除变量
使用unset
命令删除变量,但是不能删除只读变量
变量的作用域有三种:
local
标识的变量时局部变量注意:Shell中默认使用全局变量,如果变量前边没有标志,即使在函数内部定义的变量也是全局变量
环境变量
全局变量只在当前Shell进程中有效,对其他Shell进程和子进程无效,可以使用
export
命令将全局变量导出,变量就变成环境变量了.**注意:**两个没有父子关系的Shell进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即"传子不传父"
最简单的创建Shell子进程的方式是运行bash命令:
[root@cuizja learnlinux]# echo $$ #输出当前进程的PID
26746
[root@cuizja learnlinux]# bash #创建子进程
[root@cuizja learnlinux]# echo $$ #输出子进程PID
26906
[root@cuizja learnlinux]# exit #返回父进程
exit
[root@cuizja learnlinux]# echo $$ #输出进程PID
26746
下面演示一下环境变量的使用
[root@cuizja learnlinux]# a=22 #定义变量
[root@cuizja learnlinux]# echo $a #当前进程可以查看
22
[root@cuizja learnlinux]# bash #进入子进程
[root@cuizja learnlinux]# echo $a #子进程无法查看变量
[root@cuizja learnlinux]# exit #退出子进程,返回父进程
exit
[root@cuizja learnlinux]# export a #将父进程的全局变量转换为子进程的环境变量
[root@cuizja learnlinux]# bash #进入子进程
[root@cuizja learnlinux]# echo $a #子进程可以查看变量
22
如果想要在定义变量的同时导出为环境变量,可以写作
export a=22
注意
通过
export
导出的环境变量只对当前进程及其子进程有效,而且父进程关闭,父进程的环境变量也失效了,所以说这里的环境变量时临时的如果说想要让一个变量在所有进程都有效,只有将变量写入到Shell配置文件中才可以,这样在shell进程每次将启动时都会执行配置文件中的代码,做初始化工作.
命令替换的两种方式
$()
可以是执行一个命令,也可以是多个命令,多个命令用
;
隔开
注意
如果命令执行的结果包含多行(换行符),或多个连续的空白符,那么在输出变量时一定要使用双引号
""
包围,否则系统会使用默认的空白符填充,这样会导致换行无效,以及连续的空白符被压缩成一个.
在运行脚本文件abc.sh
时,我们可以给他们传递一些参数,这些参数在脚本文件内部使用$n
的形式接受,例如$1
表示第一个参数,依次类推
这里的通过
$n
的方式接受的参数就叫做位置参数如果参数个数太多,达到或者超过10个,就需要用这个方式接受
${n}
,{}
就是为了区分参数边界
给脚本文件传递位置参数
[root@cuizja learnlinux]# vim locationtest.sh
#!/bin/bash
echo $1
echo $2
[root@cuizja learnlinux]# chmod +rwx locationtest.sh
[root@cuizja learnlinux]# ./locationtest.sh var1 var2
var1
var2
[root@cuizja learnlinux]#
给函数传递位置参数
[root@cuizja learnlinux]# vim funcvartest.sh
#!/bin/bash
function func(){
echo "$1"
echo "$2"
}
func var1 var2
[root@cuizja learnlinux]# chmod +rwx funcvartest.sh
[root@cuizja learnlinux]# ./funcvartest.sh
var1
var2
特殊变量
$0
当前脚本的文件名$n(n>0)
传递脚本或函数的参数,n表示第几个参数$#
传递给脚本或函数的参数个数$*
传递给脚本或函数的所有参数$@
传递给脚本或函数的所有参数$?
上个命令的退出状态,或函数的返回值$$
当前Shell进程PID
$*
和$@
的区别
- 如果没有被双引号包围,没有区别
- 当被双引号包围时
$*
会将所有参数合并成一个参数$@
仍然将每个参数看做独立的参数,不会合并比如传递5个参数,
$*
会合并5个参数,而$@
仍然是相互独立的5个参数
[root@cuizja learnlinux]# vim vartest.sh
#!/bin/bash
echo '$*'
for var in "$*"
do
echo "$var"
done
echo '$@'
for var in "$@"
do
echo "$var"
done
[root@cuizja learnlinux]# chmod +rwx vartest.sh
[root@cuizja learnlinux]# ./vartest.sh a b c d e
$*
a b c d e
$@
a
b
c
d
e
字符串长度
${#string_name}
字符串拼接:只需将两个字符串放在一起,即可拼接字符串
${str1}${str2}
字符串截取
格式 | 说明 |
---|---|
${String: start :length} |
从String字符串的左边第start个字符开始,向右截取length个字符 |
${String: start} |
从String字符串的左边第start个字符开始截取,直到最后 |
${String:0-start:length} |
从String字符串的右边第start个字符开始,向右截取length个字符 |
${String:0-start} |
从String字符串的右边第start个字符开始截取,直到最后 |
${String#*chars} |
从String字符串第一次出现chars的位置开始,截取chars右边的所有字符 |
${String##*chars} |
从String字符串最后一次出现chars的位置开始,截取chars右边的所有字符 |
${String%chars*} |
从String字符串第一次出现chars的位置开始,截取chars左边的所有字符 |
${String%%chars*} |
从String字符串最后一次出现chars的位置开始,截取chars左边的所有字符 |
[root@cuizja learnlinux]# vim chartest.sh
#!/bin/bash
str=abcdefghijklmnopq
echo "${str:3:5}"
echo "${str:3}"
echo "${str:-3:2}"
echo "${str:-3}"
echo "${str#*de}"
echo "${str##*de}"
echo "${str%de*}"
echo "${str%%de*}"
[root@cuizja learnlinux]# ./chartest.sh
defgh
defghijklmnopq
abcdefghijklmnopq
abcdefghijklmnopq
fghijklmnopq
fghijklmnopq
abc
abc
Bash Shell只支持一维数组
数组定义的一般形式为:`array_name=(ele1,ele2,ele3,…)
Shell是弱类型的,数组中的元素类型不用必须相同
Shell数组的长度是不固定的,定义完成后可以直接增加元素
数组增加元素
直接array_name[index]=value
就可以给数组增加元素
[root@cuizja learnlinux]# arr=(1 2 3)
[root@cuizja learnlinux]# echo ${arr[*]}
1 2 3
[root@cuizja learnlinux]# arr[3]=4
[root@cuizja learnlinux]# echo ${arr[*]}
1 2 3 4
数组部分赋值
array_name=([i]=value [j]=value ....)
这样既可将数组对应的位置赋值,其他位置不赋值
获取数组元素
${array_name[index]}
${array_name[*]}
,${array_name[@]}
获取数组长度
数组长度的获取和字符串长度的获取相同,都是使用
#
获取
${#array_name[@]}
${#array_name[*]}
这里我简单的理解为${#变量名}
就是求长度的运算
数组的合并就是讲两个数组连接成一个数组
**思路:**先利用
@
或*
将数组扩展成列表(扩列操作),然后再合并到一起
格式
array_new=(${array1[@]} ${array2[@]})
array_new=(${array1[*]} ${array2[*]})
使用
unset
关键字删除数组元素
格式
unset array_name[index]
删除单个元素unset array_name
删除整个数组Shell内建命令,就是由Bash自身提供的命令,而不是文件系统中的某个可执行文件
可以使用type
来确定一个命令是否为内建命令
[root@cuizja learnlinux]# type cd
cd is a shell builtin
[root@cuizja learnlinux]# type ifconfig
ifconfig is /usr/sbin/ifconfig
Bash Shell中内建命令列表
命令 | 说明 |
---|---|
: |
扩展参数列表,执行重定向操作 |
. |
读取并执行指定文件中的命令(在当前shell环境中) |
alias |
为命令定义一个别名 |
bg |
将文件以后台模式运行 |
bind |
将键盘序列绑定到一个readline函数或宏 |
break |
退出for,while,select或until循环 |
builtin |
执行指定的shell内建命令 |
caller |
返回子函数调用的上下文 |
cd |
切换文件目录 |
command |
执行命令,无需进行通常的shell查找 |
compgen |
为指定单词生成可能的补全匹配 |
complete |
显示指定的单词是如何补全的 |
compopt |
修复指定单词的补全选项 |
continue |
继续执行for,while,select或until下一次循环 |
declare |
声明一个变量或变量类型 |
dirs |
显示当前存储目录的列表 |
disown |
从进程作业表中删除指定的作业 |
echo |
输出内容到STDOUT |
enable |
启用或禁用指定的内建shell命令 |
eval |
将指定的参数拼接成一个命令,然后执行该命令 |
exec |
用指定命令替换shell进程 |
exit |
强制shell以指定的状态码退出 |
export |
设置子shell进程可用的变量 |
fc |
从历史记录中选择命令列表 |
fg |
将作业以前台模式运行 |
getopts |
分析指定的位置参数 |
hash |
查找并记住指定命令的全路径名 |
help |
显示帮助文件 |
history |
显示命令历史记录 |
jobs |
列出活动作业 |
kill |
想指定的进程ID发送一个系统信号 |
let |
计算一个数学表达式中的每个参数 |
local |
创建局部变量 |
logout |
退出登录shell |
mapfile |
从STDID读取数据行,并将其假如索引数组 |
popd |
从目录栈中删除记录 |
printf |
使用格式化字符串显示文本 |
pushd |
向目录栈中添加一个目录 |
pwd |
显示当前工作目录的路径名 |
read |
从STDIN读取一行数据并将其赋值给一个变量 |
readarray |
从STDIN读取数据行并将其放入索引数组 |
readonly |
只读变量 |
return |
强制函数以某个值退出,这个值可以被调用的脚本获取 |
set |
设置并显示环境变量的值和shell属性 |
shift |
将位置参数依次下降一个位置 |
shopt |
打开/关闭控制shell可选行为的变量值 |
source |
读取并执行指定文件中的命令 |
suspend |
暂停shell的执行,知道收到一个SIGCOUT信号 |
test |
基于指定条件返回退出状态码0或1 |
times |
显示累计的用户和系统时间 |
trap |
如果收到了指定的系统信号,执行指定的命令 |
type |
显示命令类型 |
typeset |
声明一个变量或变量类型 |
ulimit |
为系统用户设置指定的资源的上限 |
umask |
为新建的文件和目录设置默认权限 |
unalias |
删除指定的别名 |
unset |
删除环境变量或shell属性 |
wait |
等待指定的进程完成,并返回退出状态码 |
格式
read [-options] [variables]
选项
-a array
把读取的数据赋值给数组array,下标从0开始-d key
用字符串key指定读取结束位置,不包含key-e
输入的同时识别转义符-n num
读取num个字符-p prompt
显示提示信息,提示内容为prompt-r
原样读取,不识别转义字符-s
静默模式,输入的内容不会显示在界面-t seconds
设置超时时间,超时后返回一个非0的退出状态,表示读取失败-u fd
使用文件描述符fd作为输入源,而非标准输入,类似于重定向格式
declare +/- [选项] [变量名=变量值]
-
表示设置属性,+
表示取消属性
选项
-f name
列出之前由用户在脚本中定义的函数名和函数体-F name
仅列出自定义函数名称-g name
在shell函数内部创建全局变量-p name
显示指定变量的属性和值-a name
声明变量为普通数组-A name
生命变量为关联数组(支持索引下标为字符串)-i name
将变量定义为整数型-r name
将变量定义为只读,等价于readonlyShell中的运算符和其他编程语言基本相同,但是和其他语言不同的是,Shell编程进行数学计算必须使用数学计算命令,下边列举一些数学计算的命令
数学计算命令
运算操作符/运算命令 | 说明 |
---|---|
(()) |
用于整数运算,效率很高 |
let |
用于整数运算,和(()) 相似 |
$[] |
用于整数运算,不如(()) 灵活 |
expr |
可用于整数运算,也可以处理字符串,比较麻烦,需要注意各种细节 |
bc |
Linux下的计算器程序,可以处理整数和小数,Shell只支持整数运算,如果想要计算小数,必须使用bc 这个外部计算器 |
declare -i |
将变量定义为整数,然后就可以直接进行基础的数学运算(仅支持最基本的数学运算,加减乘除取余等) |
格式
if condition
then
statements
fi
condition是判断条件,条件成立,执行then后边的语句,否则直接结束判断
注意,这里必须使用
fi
结束if语句
也可以将if和then写在一行
if condition; then
statements
fi
注意,当if和then写在一行的时候,condition后边的分号
;
不能省略
格式
if condition
then
statement1
else
statement2
fi
如果condition条件成立,执行then后边,否则执行else后边
类似于java中的if - else if - else结构
格式
if condition1
then
statement1
elif condition2
then
statement2
else
statement2
fi
注意,if和elif后边都必须有then
test命令有很多选项,可以进行数值,字符串和文件三个方面的检测
格式
test expression
表达式成立,退出状态为0,否则为非0
可以简写为[]
[ expression ]
在expression前后需要有空格,并且空格是必须的
test命令的选项
-b filename
判断文件是否存在,并且是否为块设备文件-c filename
判断文件是否存在,并且是否为字符设备文件-d filename
判断文件是否存在,并且是否为目录文件(是否是文件夹)-e filename
判断文件是否存在-f filename
判断文件是否存在,并且是否为普通文件(文件)-L filename
判断文件是否存在,并且是否为符号链接文件-p filename
判断文件是否存在,并且是否为管道文件-s filename
判断文件是否存在,并且是否为非空-S filename
判断文件是否存在,并且是否为套接字文件-r filename
判断文件是否存在,并且是否拥有读权限-w filename
判断文件是否存在,并且是否拥有写权限-x filename
判断文件是否存在,并且是否拥有执行权限-u filename
判断文件是否存在,并且是否拥有SUID权限-g filename
判断文件是否存在,并且是否拥有SGID权限-k filename
判断文件是否存在,并且是否拥有SBIT权限filename1 -nt filename2
判断文件1的修改时间是否比文件2的新newfilename1 -ot filename2
判断文件1的修改时间是否比文件2的旧oldfilename1 -ef filename2
判断文件1和文件2是否同一个(本质上比较文件inode是否相等)num1 -eq num2
判断1和2是否相等num1 -ne num2
判断1和2是否不等num1 -gt num2
判断1是否大于2num1 -lt num2
判断2是否小于2num1 -ge num2
判断1是否大于等于2num1 -le num2
判断1是否小于等于2-z str
判断str是否为空-n str
判断str是否非空str1=str2
str1==str2
=
和==
是等价的,都用来判断字符串是否相等str1 != str2
判断是否不等str1\>str2
判断1是否大于2,\
是转义字符,防止>
被当做重定向运算符str1\ 判断1是否小于2
expression1 -a expression2
逻辑与,同时成立expression1 -o expression2
逻辑或,有一个成立就成立!expression
逻辑非,取反注意 Shell中不支持>= 和<=运算符
[[]]
和test类似,test能做到的[[]]
也能做到,test不能做到,[[]]
也能做到,可以把他当做test的升级版
格式
[[ exptression ]]
注意,expression前后需要加空格
与test的区别
变量名不需要用双引号""
包围
不能对>,<进行转义,也不需要转义
支持&&,||,!等逻辑运算符
支持正则表达式判断
[[ str =~ regex ]]
[[]]更强大,建议用它替代test
格式
case expression in
pattern1)
statement1
;;
pattern2)
statement2
;;
*)
statement
esac
expression 可以是一个变量,数字,字符串,命令执行结果
pattern可以是一个数字,字符串甚至简单的正则表达式,正则表达式用[regex]
格式
while condition
do
statements
done
执行流程
先condition判断,成立进入循环,每次执行完statement之后,重新判断condition,不成立时结束.
与while循环恰恰相反,不成立时进行循环,一旦成立终止循环
格式
until condition
do
statements
done
执行顺序
先condition判断,不成立进入循环,每次执行完statement之后,重新判断condition,成立时结束.
C语言风格的for循环
for((exp1;exp2;exp3))
do
statements
done
执行过程
1.先执行exp1
2.在执行exp2,如果判断结果成立,这执行statement,否则结束循环
3.执行完循环体再执行exp3
4.重复执行2,3步骤,直到2的结果不成立
Python风格的for in 循环
格式
for variable in value_list
do
statements
done
相当于java中的增强for循环
value_list的书写形式
1 2 3 4
,"abc" "def" "ghi"
{start..end}
,例如{1..100}
``
或者$()
命令的执行结果*.sh
$#
,$*
,$@
,$?
,$$
等与for in 循环类似,但是他可以将value_list的内容输出,供用户选择
格式
select variable in value_list
do
statements
done
ctrl+D结束循环
break格式
break n
n表示循环成熟,省略n表示跳出当前循环
continue格式
continue n
n表示循环层数,省略表示跳过本次循环,直接进入下一次循环
如果n有值,表示内层循环直接跳过本次循环,外层循环也跳过本次循环
语法格式
function funcname(){
statements
[return value]
}
function
定义函数关键字
funcname
函数名
statement
函数中要执行的代码
return value
返回值
也可以简化为这样
funcname(){
statements
[return value]
}
function funcname{
statements
[return value]
}
函数调用
funcname
funcname param1 param2 param3
函数参数接收方式
$n
使用位置参数接收$#
可以获取参数个数$@
或者$*
可以一次性获取所有参数在之前的文章中的管道内容的补充
管道中的常用命令
命令 | 说明 |
---|---|
awk |
用于文本处理的解释性程序设计语言,通常被用作数据提取和报告的工具 |
cut |
用于将每个输入文件(如果没有指定文件则为标准输入)的每行的指定部分输出到标准输出 |
grep |
用于搜索一个或多个文件中指定模式的行 |
tar |
用于归档文件的应用程序 |
head |
用于读取文件的开头部分(默认是10行),如果没有指定未见,则从标准输入读取 |
paste |
用于合并文件的行 |
sed |
用于过滤和转换文本的流编辑器 |
sort |
用于对文本文件的行进行排序 |
split |
用于将文件分割成块 |
strings |
用于打印文件中可打印的字符串 |
tac |
与cat命令功能相反,用于倒序的显示文件或链接文件 |
tail |
用于显示文件的结尾部分 |
tee |
用于从标准输入读取内容写入到标准输出和文件 |
tr |
用于转换或删除字符 |
uniq |
用于报告或忽略重复的行 |
wc |
用于打印文件中的总行数,单词或字节数 |