shell
shell是用户使用Linux的桥梁,连接了用户和Linux内核。我们可以通过shell命令操作和控制操作系统,并且更高效更安全。
shell基础概念
shell是一个命令解释器,同时也是一门编程语言,不仅提供内核和设备交互的办法,还继承了一些设计模式。
作为命令解释器,它提供接口,把用户输入的命令进行解释并送入内核。而作为作为一门编程语言,虽然它没有C、Java那么强大,但是我们熟知的选择、循环、变量、函数等等都是具备的。
shell编程语言
- 脚本解释器
- 变量
- 用户输入
- 测试
- 条件判断
- 循环语句
- 脚本参数传递
- 退出状态码
- 逻辑操作符
- 函数
- 通配符
- 调试
-
shell编程语言
shell是一种脚本语言,它一边执行一边翻译,不会产生可执行文件。编写代码后不需要编译,直接运行即可。
变量
变量类型有三种,自定义变量、环境变量、系统环境变量。
- 自定义变量:作用域是当前的shell,使用
=声明。
变量名=变量值,如num=2
撤销变量:unset 变量名
声明静态变量:readonly 变量
复制代码
- 环境变量:作用域是当前shell与其子shell,使用
exportdeclare-x声明。使该变量在子shell也可用。
export 变量
declare -x 变量
复制代码
declare有许多选项,-a、-i等等。带有-的是给变量设定类型属性,+的是取消变量的类型属性。
- 系统环境变量:所有shell,启动加载声明。
运算符和引用
运算符分为算数运算符、逻辑运算符、比较运算符、引号、圆括号、命令连接、后台运行。
- 算数运算符和逻辑、比较运算符,和常识一样。不作赘述。
- 引号分为双引号、单引号和反引号。
双引号是部分引用,使用双引号时,$、反引号、转义符还是解析为特殊意义
单引号为完全引用,直接输出
反引号是执行命令
复制代码
- 命令连接三个符号||、&&、;,||前命令返回非0继续执行||符号后的命令。&&要返回为0才继续执行。;是串行执行。
- 后台运行用&符号,让其后的命令在后台运行。
脚本解释器
你可以从上面脚本的第一行看到 #!/bin/bash 这行指定了你的程序将使用那个解释器,基本上是将路径引用到解释器。Linux/Unix中有很多解释器,其中一些是:bash,zsh,sh,csh和ksh等。
这里推荐一个玩命令行必须知道的一个开源项目oh-my-zsh
All the best people in life seem to like LINUX. — Steve Wozniak
查看你的系统中有那些脚本解释器
cat /etc/shells
bash: #!/bin/bash
zsh: #!/bin/zsh
ksh: #!/bin/ksh
csh: #!/bin/csh
and so on…
注意⚠️
如果脚本不包含解释器,则使用你的默认shell执行命令,因此代码可能正常运行,虽然是这样,但是不推荐这样做,使用echo $SHELL可以知道你当前使用的解释器
注释
注释以#开始,#后面的内容会被解释器忽略,但是#!另当别论
变量
变量指向内存中的一块区域,变量有对应的变量名和值,可以存储一些可以在将来更改的数据,shell中定义变量不需要指定变量的类型
VARIABLE_NAME="Value"
当命名一个变量是你必须记得以下几点
- 变量名是区分大小写的
- 为了方便,变量名最好大写
- 要使用变量,必须在变量前面加
$符号
例子🌰
#!/bin/bash
MY_NAME="shellhub"
echo "Hello, I am $MY_NAME"
OR
#!/bin/bash
MY_NAME="shellhub"
echo "Hello, I am ${MY_NAME}"
提示: 可以把命令执行后的输入结果赋值给一个变量
LIST=$(ls)
SERVER_NAME=$(hostname)
合法的变量名
THIS3VARIABLE=”ABC”
THIS_IS_VARIABLE=”ABC”
thisIsVariable=”ABC”
不合法的变量名
4Number=”NUM”
This-Is-Var=”VAR”
# No special character apart from underscore is allowed!
用户输入
read 命令接收键盘的输入,标准输入(Standard Input)
read -p "PROMPT MESSAGE" VARIABLE
其中PROMPT MESSAGE为提示用户的信息,变量VARIABLE可以保存用户的输入,可以在程序中使用该变量
#!/bin/bash
read -p "Please Enter You Name: " NAME
echo "Your Name Is: $NAME"
测试
测试主要用于条件判断。[ condition-to-test-for ] ,如[ -e /etc/passwd ],注意的是[]前后必须有空格,如[-e /etc/passwd]是错误的写法
- 文件测试操作
-d FILE_NAM # True if FILE_NAM is a directory
-e FILE_NAM # True if FILE_NAM exists
-f FILE_NAM # True if FILE_NAM exists and is a regular file
-r FILE_NAM # True if FILE_NAM is readable
-s FILE_NAM # True if FILE_NAM exists and is not empty
-w FILE_NAM # True if FILE_NAM has write permission
-x FILE_NAM # True if FILE_NAM is executable
- 字符串测试操作
-z STRING # True if STRING is empty
-n STRING # True if STRING is not empty
STRING1 = STRIN2 # True if strings are equal
STRING1 != STRIN2 # True if strings are not equal
- 算术测试操作
var1 -eq var2 # True if var1 is equal to var2
var1 -ne var2 # True if var1 not equal to var2
var1 -lt var2 # True if var1 is less than var2
var1 -le var2 # True if var1 is less than or equal to var2
var1 -gt var2 # True if var1 is greater than var2
var1 -ge var2 # True if var1 is greater than or equal to var2
条件判断
和其他编程语言一样,shell脚本也能基于条件进行判断,我们可以使用if-else或if-elif-else
Avoid the Gates of Hell. Use Linux!
if语句
if [ condition-is-true ]
then
command 1
command 2
...
...
command N
fi
if-else
if [ condition-is-true ]
then
command 1
elif [ condition-is-true ]
then
command 2
elif [ condition-is-true ]
then
command 3
else
command 4
fi
case语句
case可以实现和if一样的功能,但是当条件判断很多的时候,使用if不太方便,比如使用if进行值的比较
case "$VAR" in
pattern_1)
# commands when $VAR matches pattern 1
;;
pattern_2)
# commands when $VAR matches pattern 2
;;
*)
# This will run if $VAR doesnt match any of the given patterns
;;
esac
例子🌰
#!/bin/bash
read -p "Enter the answer in Y/N: " ANSWER
case "$ANSWER" in
[yY] | [yY][eE][sS])
echo "The Answer is Yes :)"
;;
[nN] | [nN][oO])
echo "The Answer is No :("
;;
*)
echo "Invalid Answer :/"
;;
esac
迭代语句 - 循环
可以通过循环执行同一个代码块很多次
for循环
for VARIABLE_NAME in ITEM_1 ITEM_N
do
command 1
command 2
...
...
command N
done
Example
#!/bin/bash
COLORS="red green blue"
for COLOR in $COLORS
do
echo "The Color is: ${COLOR}"
done
Another Example
for (( VAR=1;VAR<N;VAR++ ))
do
command 1
command 2
...
...
command N
done
在当前所有txt文件前面追加new实现重命名
#!/bin/bash
FILES=$(ls *txt)
NEW="new"
for FILE in $FILES
do
echo "Renaming $FILE to new-$FILE"
mv $FILE $NEW-$FILE
done
while循环
当所给的条件为true时,循环执行while里面的代码块
while [ CONNDITION_IS_TRUE ]
do
# Commands will change he entry condition
command 1
command 2
...
...
command N
done
判断条件可以是任意的测试或者命令,如果测试或命令返回0,则表示条件成立,如果为非0则退出循环,如果一开始条件就不成立,则循环永远不会执行。
如果你不知道退出状态码是什么请不要担心,我后面会告诉你 :)
例子 一行一行读取文件内容
#!/bin/bash
LINE=1
while read CURRENT_LINE
do
echo "${LINE}: $CURRENT_LINE"
((LINE++))
done < /etc/passwd
# This script loops through the file /etc/passwd line by line
注意⚠️
continue 用于结束本次循环
break 用于结束整个循环
参数传递
当我们运行脚本的时候,可以传递参数供脚本内部使用$ ./script.sh param1 param2 param3 param4
这些参数将被存储在特殊的变量中
$0 -- "script.sh"
$1 -- "param1"
$2 -- "param2"
$3 -- "param3"
$4 -- "param4"
$@ -- array of all positional parameters
这些变量可以在脚本中的任何地方使用,就像其他全局变量一样
退出状态码
任何一个命令执行完成后都会产生一个退出状态码,范围0-255,状态码可以用来检查错误
- 0 表示正确执行并正常退出
- 非0表示执行过程中出错,没有正常退出
上一条命令执行后的退出状态码被保存在变量$?中
例子 使用ping检查主机和服务器之间是否可以抵达
#!/bin/bash
HOST="google.com"
ping -c 1 $HOST # -c is used for count, it will send the request, number of times mentioned
RETURN_CODE=$?
if [ "$RETURN_CODE" -eq "0" ]
then
echo "$HOST reachable"
else
echo "$HOST unreachable"
fi
自定义退出状态码
默认的状态码是上一条命令执行的结果,我们可以通过exit来自定义状态码
exit 0
exit 1
exit 2
...
...
exit 255
逻辑操作符
shell脚本支持逻辑与和逻辑或
逻辑与 &&
逻辑或 ||
Example
mkdir tempDir && cd tempDir && mkdir subTempDir
这个例子中,如果创建tempDir成功,执行后面的cd,继续创建subTempDir
函数
可以把一些列的命令或语句定义在一个函数内,从程序的其他地方调用
注意⚠️
- 函数包含了一些列你要重复调用的指令(函数必须先定义后调用)
- 把函数定义在程序开始或主程序之前是一个最佳实践
语法
function function_name() {
command 1
command 2
command 3
...
...
command N
}
调用函数 简单的给出函数名字
#!/bin/bash
function myFunc () {
echo "Shell Scripting Is Fun!"
}
myFunc # call
函数参数传递
和脚本一样,也可以给函数传递参数完成特殊的任务,第一个参数存储在变量$1中,第二个参数存储在变量$2中...,$@存储所有的参数,参数之间使用空格分割 myFunc param1 param2 param3 ...
变量的作用范围
全局变量: 默认情况下,shell中的变量都定义为全局变量,你可以从脚本中的任何位置使用变量,但是变量在使用之前必须先定义
本地变量: 本地变量只能在方法内部访问,可以通过local关键词定义一个本地变量,定义一个本地变量的最佳实践是在函数内部
通配符
使用通配符可以完成特定的匹配
一些常用的通配符
* 可以通配一个或多个任意字符
*.txt
hello.*
great*.md
?匹配一个字符
?.md
Hello?
[]匹配括号内部的任意一个字符
He[loym], [AIEOU]
[!]不匹配括号内的任何字符
`[!aeiou]`
预先定义的通配符
- [[:alpha:]]
- [[:alnum:]]
- [[:space:]]
- [[:upper:]]]
- [[:lower:]]
- [[:digit:]]
匹配通配符 有些情况下我们想匹配*或?等特殊字符,可以使用转义字符
*
?
调试
bash提供一些可选项帮助你调试程序
Some Options:
-xoption
它在执行时打印命令和参数。它被称为打印调试,跟踪或x跟踪。我们可以通过修改第一行来使用它-eoption
它代表“出错”。如果命令以非零退出状态退出,这将导致脚本立即退出。-voption
它在读取时打印shell命令/输入行。
**注意:**这些选项可以组合使用,一次可以使用多个选项!
#!/bin/bash-xe
#!/bin/bash-ex
#!/bin/bash-x-e
#!/bin/bash-e-x