前言:
突然发现Linux下执行ca$1t flag命令时也可以正常访问,再加上以前做题时也有使用这些奇怪符号绕过的。得知是shell脚本相关得到那么我就来看看shell脚本吧。
什么是 Shell
shell 是一个应用程序,这个应用程序提供了一个界面,可以使用户通过这个界面访问 Linux 内核的服务。
我们常常说的Shell 其实更多的是指 Shell 脚本(shell script),是一种为shell编写的脚本程序,一般文件后缀为.sh
其实Shell 编程跟 java、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
Shell 运行的模式
Shell 运行模式有 交互式和非交互式两种模式,我们日常接触的应该是交互式的。
交互式:这种模式就和我们执行命令行一样,执行一条命令立马返回结果。
非交互模式:在这种模式下,shell从文件或者管道中读取命令并执行。当 shell 解释器执行完文件中最后一个命令后,shell进程中止,并回到父进程。
第一个 Shell 脚本
编写
打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好。如编写以下代码:
1 | #!/bin/sh |
解析代码:
- 第一行,“#!”是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行。如果要查看系统中安装的shell,可以使用
cat /etc/sehlls
命令来查看。 - 第二行,创建一个目录mayi077 第3行进入这个目录。
- 第四行到最后,是一个循环语句循环3次每次创建一个test_${i}.txt 文件,其中${i}是变量。然后将那串字符输出重定向到 test_${i}.txt 文件中。其中
>>
和>
的区别就是将前者是追加写入后者是覆盖写入。
shell 脚本的运行
- 作为可执行程序,我们可以通过
ll
查看刚刚写入的文件是没有可执行权限的。我们使用chmod +x 1.sh
来给它加上执行权限,然后是使用./1.sh
来执行。 - 作为解释器参数,直接运行解析器,其参数就是shell脚本的文件名,我们使用
/bin/sh 1.sh
即可以运行。
Shell 中的变量
Shell中的变量分为用户自定义变量,环境变量,位置参数变量和预定义变量。可以通过set命令查看系统中存在的所有变量,也可使用unset 来删除变量。
系统变量: 保存和系统操作环境相关的数据。HOME、PWD、SHELL、USER等等。
位置参数变量:主要用来向脚本中传递参数或数据,变量名不能自定义,变量作用固定。
预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
用户自定义变量
变量设置及赋值:
和其它语言命名规则一致,值得注意的有:
- 在bash中,变量的默认类型都是字符串型,如果要进行数值运算,则必须指定变量类型为数值型。
- 变量用等号连接值,等号左右两侧不能有空格。
- 变量的值如果有空格,需要使用单引号或者双引号包括。
如: 将一个命令的执行结果赋值给变量:1
2a=`ls -la` ##反引号,运行里面的命令,并把结果返回给变量
a=$(ls -la) ## 等价于反引号
调用变量:
使用变量时只需要在变量名前加上 $ 符即可调用。
环境变量
用户自定义变量只在当前的shell中生效,而环境变量会在当前shell和其所有子shell中生效。如果把环境变量写入相应的配置文件,那么这个环境变量就会在所有的shell中生效。
定义修改环境变量的方法:
export 变量名=变量值 作用域:当前shell以及所有的子shell。
位置参数变量
$n
n为数字,0代表命令本身,1-$9代表第一到第9个参数,十以上的参数需要用大括号包含,如${10}。$*
代表接收所有的参数,将所有参数看作一个整体。以”12 … $n”的形式输出所有参数。$@
代表接收的所有参数,将每个参数区别对待。以”1””2” … “$n” 的形式输出所有参数。$#
代表接收的参数个数。
预定义变量
$?
最后一次执行的命令的返回状态。如果这个变量的值为 0,则证明上一条命令正确执行;如果这 个变量的值为非 0 (具体是哪个数由命令自己来决定),则证明上一条命令执行错误$$
当前进程的进程号(PID)$!
后台运行的最后一个进程的进程号(PID)
流程控制
条件语句
- if 语句
- 如果中括号里的表达式为真,那么
then
和fi
之间的代码会被执行。fi标志着条件代码块的结束。值得注意的是1
2
3
4
5
6
7
8
9
10
11
12# 写成一行
if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi
# Output: 1 -eq 1 result is: true
# 写成多行
if [[ "abc" -eq "abc" ]];
then
echo ""abc" -eq "abc" result is: true";
fi
# Output: abc -eq abc result is: true
#貌似如果写成多行的话 分号 可有可无,因为根据换行已经可以区分了。
# 也就是说换行和分号都可以用来做分界用-eq
(==哪些也还是可用的,只是要了解下这种方式):
- 如果中括号里的表达式为真,那么
- if…else :
1
2
3
4
5
6if [[ 2 -ne 1 ]]; then
echo "true"
else
echo "false"
fi
# Output: true - if elif else
1
2
3
4
5
6
7
8
9
10x=10
y=20
if [[ ${x} > ${y} ]]; then
echo "${x} > ${y}"
elif [[ ${x} < ${y} ]]; then
echo "${x} < ${y}"
else
echo "${x} = ${y}"
fi
# Output: 10 < 20
循环语句
- for
1
2
3
4for arg in elem1 elem2 ... elemN
do
### 语句
done - while
1
2
3
4while [[ condition ]]
do
### 语句
done - until 跟while循环正好相反。它跟while一样也需要检测一个测试条件,但不同的是,只要该条件为 假 就一直执行循环:
1
2
3
4
5
6
7
8
9
10
11x=0
until [[ ${x} -ge 5 ]]; do
echo ${x}
x=`expr ${x} + 1`
done
# Output:
# 0
# 1
# 2
# 3
# 4 - select 循环帮助我们组织一个用户菜单。它的语法几乎跟for循环一致:
1
2
3
4select answer in elem1 elem2 ... elemN
do
### 语句
done
文件包含
可以使用source和.关键字,如:
1 | source ./function.sh |
在bash里,source和.是等效的,他们都是读入function.sh的内容并执行其内容(类似PHP里的include),为了更好的可移植性,推荐使用第二种写法。