linux-shell

shell是一个应用程序,它连接了用户和内核。使用户可以更高效,安全的使用内核。这就是linux命令行的本质。

shell也是一个命令解释器,也被称为shell编程。

shell——应用程序

shell script=shell script脚本

#!/bin/bash
shell脚本的标识符,如果你要使用其他的shell程序来运行就修改它。
例如我要用/usr/bin/zsh来运行这个脚本,那我就使用#!/usr/bin/zsh作为开头

确认自己使用的shell是什么版本

1
echo $SHELL

确认shell的版本

1
2
3
$SHELL --version
zsh --version
bash --version

基本语法

shell脚本执行方式

1
2
3
4
5
6
1.第一种
chmod +x 1.sh
./1.sh

2.第二种
/bin/bash 1.sh

建议在主目录新建一个/bin子目录,专门存放可执行脚本,然后把/bin加入$PATH。

1
export PATH=$PATH:~/bin

上面命令改变环境变量$PATH,将/bin添加到$PATH的末尾。可以将这一行加到/.bashrc文件里面,然后重新加载一次.bashrc,这个配置就可以生效了。

1
$ source ~/.bashrc

以后不管在什么目录,直接输入脚本文件名,脚本就会执行。

1
$ script.sh

上面命令没有指定脚本路径,因为script.sh在$PATH指定的目录中。

文件后缀名不影响使用。

变量定义

定义变量不加$符号

shell变量命名格式:

  • 变量名和等号之间不能有空格
  • 命名不能用数字开头,中间不能有空格
  • 不能使用标点符号,关键字

分号;

语句结束符,上一条命令执行结束后(不管成功与否,都会执行第二条命令

变量使用

1
2
a=hello
echo ${a}

变量名外的花括号是可选的,建议加上,避免某些变量名被识别成字符串。

只读变量

使用readonly使变量变为只读。

1
2
a=hello
readonly a

只读后将不能修改变量的值。

删除变量

unset a
unset不能删除只读变量.

变量类型

1.局部变量

仅当前shell有效,重启后失效

2.环境变量

所有程序都能用环境变量

3.shell变量

shell程序设置的特殊变量

shell字符串

单引号

a=’hello’

字符串可以用单引号,也可以用双引号,也可以不用引号。
单引号字符串的变量无效,双引号可以。
单引号字符串成对出现,作为字符串拼接使用。

双引号

双引号可以有变量,也可以出现转义字符。

获取字符串长度

获取字符串长度
$(#string)

1
2
a='zxcv'
echo ${#a}

截取字符串
${string:2:4}

${string%.*} 取得文件名

${string#*.} 取得文件的后缀名

注释

#这就是注释。

传参

$0 脚本的文件名
$1 第一个参数
$2 第二个
$3 第三个

其他的特殊字符

$#传递到脚本的参数的个数
$*以整体输出所有参数
[email protected]单独输出每个参数
$?函数的返回值

数组

数组下标从0开始,shell不支持多维数组。
shell数组用括号标识,元素用空格分割。

定义一个数组
a=('a' 'b' 'c' 'd')

使用数组
1
2
a=('a' 'b' 'c' 'd')
echo ${a[1]}
获取数组长度
1
2
3
${#array[#*]} #输出数组长度
${array[@]} #一次性输出数组内容
${array[*]} #一次性输出数组内容

运算符

运算符 说明 例子
\* 乘法 expr $a \* $b
% 取余 expr $a % $b
= 赋值 a=$b
== 等于 [ $a == $b ] 条件判断 返回False
!= 不等于 [ $a != $b ] 条件判断 返回True

乘法需要转义 \*
shell中计算要用反引号``
为什么?

关系运算
运算符 说明
-eq 检测两个数是否相等
-ne 检测两个数是否不相等
-gt 检测左边的数是否大于右边的,如果是,则返回 true。
-lt 等于
不等于
布尔运算
运算符 说明
-o 或运算,又一个表达式为true就返回true
-a 与运算,两个表达式为true才返回true
! 非运算,表达式为true返回false,否则返回true.
逻辑运算
运算符 说明
&& 逻辑AND
|| 逻辑OR
文件测试运算符
运算符 说明
-d 检测目录
-f 检测是不是文件
-r -w -x 检测文件权限
-g -u 检测SGID/SUID位

算术运算

(())可以进行整数的算术运算,并且自动忽略内部的空格
((…))语法支持的算术运算符如下。

  • +:加法
  • -:减法
  • *:乘法
  • /:除法(整除)
  • %:余数
  • **:指数
  • ++:自增运算(前缀或后缀)
  • –:自减运算(前缀或后缀)

i++ 和 ++i 的区别
i++是先使用变量i,然后再自增
++i是先让i自增,再使用变量i

((…))结构可以嵌套,也可以在内部用圆括号改变运算顺序。

1
2
3
4
5
$ echo $(((5**2) * 3))
75
# 等同于
$ echo $(($((5**2)) * 3))
75

echo

原样输出
1
2
echo hello world
hello world
输出多行文本
1
2
3
4
5
echo "asdasd
asdasd
asdasdad
asdasdasd
"
echo -n

echo默认换行,使用-n参数可以取消换行

开启转义 -e
1
echo -e "OK!\n" # -e 开启转义

read

read

1
2
3
#!/bin/bash
read name
echo "hello $name"

read 的参数

  • -t 超时秒数,如果超过了和指定时间,用户仍然没有输入,脚本将继续向下执行。
  • -p 指定用户输入时的提示信息
  • -a 将用户输入的值赋值给一个数组
  • -n 指定值读取若干个字符作为变量值,而不是整行读取
  • -e 允许用户输入时,使用readline提供的快捷键

printf

当参数大于前面的规则时,还是会按照前面的输出
printf输出默认不换行

换行需要用\n

格式化

%s字符串 %-10s左对齐,没有就右对齐
%f浮点数 %9.2f .2是小数部分
%d整数

test

参数 说明
-eq 等于
-ne 不等于
-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于
test字符串测试
1
2
3
4
5
test 表达式

[ 表达式 ]

[[ 表达式 ]]

第三种支持正则表达式。
使用第二种[]和第三种[[]]在表达式的前后都要有空格

其实[也是一个特殊命令,用type -a [可以看到有个[ is '/bin/[',这也解释了为什么[后面要有空格。

if

if的else语句不能为空,如果没有执行条件就不要写.

if
1
2
3
4
5
6
a=10
b=20
if [ $a -eq $b ];
then
echo "相等"
fi
if-else
1
2
3
4
5
6
7
8
a=10
b=20
if [ $a -eq $b ];
then
echo "相等"
else
echo "不相登"
fi
if else-if else
1
2
3
4
5
6
7
8
9
10
11
12
a=10
b=20

if [ $a -eq $b]
then
echo "相等"
elif [ $a -gt $b ]
then
echo "大于"
else
echo "???"
fi

for循环

格式1

1
2
3
4
for var in "item1 item2 itmeN"
do
echo "$var"
done

格式2

1
2
3
4
for ((i=1;i<=5;i++))
do
echo "$i"
done

for的无限循环
for (( ;; ))

while循环

两种无限循环

1
2
3
4
while true
do
echo "a"
done
1
2
3
4
while:
do
echo "a"
done

until循环

until条件为false时才执行,当条件为true不执行。

case

1
2
3
4
5
6
7
8
9
10
11
12
13
case var in
1)
echo ""
echo "ss"
;;
2)
echo "ss"
echo "aa"
;;
*)
echo "??"
;;
esac

break 跳出循环

break跳出所有循环

continue

跳出当前循环

函数

1
2
3
4
function name(){
command #语句
[return value] #返回值,可指定返回值,也可以不写
}

$?调用函数的返回值

1
2
3
4
5
6
7
function c(){
echo 'aaa'
b="123"
retun $b
}
c
echo $?

通配符

符号 含义
~ 家目录
~+ 当前所在目录
? 匹配文件路径里面的任意单字符,但不匹配空字符,如果需要匹配多个任意单个字符,可以用多个?
* 匹配文件路径里的任意数量字符,包括0个字符
** 和*一样,但是区别在于**是用于匹配零个或多个子目录
[] 匹配括号内的任意一个字符,方括号是文件名匹配,开展后的结果必须符合现有文件的路径,如果不存在,就不进行扩展。
[start-end] 匹配一个范围.
{1,2,3} 分别扩展大括号里面的值,大括号内部不能有空格。大括号可以嵌套。{p{n,h}{p,g}},也可以于其他字符连用。
{start..end} 连续序列,范围。touch {1..99}.txt,也可以{99..1}
$变量
$() 子命令扩展
`` 同上
(()) 算术扩展
[[:class:]] 表示某一类特定字符

start-end扩展的常用例子

  • [a-z]:所有小写字母。
  • [a-zA-Z]:所有小写字母与大写字母。
  • [a-zA-Z0-9]:所有小写字母、大写字母与数字。
  • [abc]*:所有以a、b、c字符之一开头的文件名。
  • program.[co]:文件program.c与文件program.o。
  • BACKUP.[0-9][0-9][0-9]:所有以BACKUP.开头,后面是三个数字的文件名

常用[[:class:]]字符类

  • [[:alnum:]]:匹配任意英文字母与数字
  • [[:alpha:]]:匹配任意英文字母
  • [[:blank:]]:空格和 Tab 键。
  • [[:cntrl:]]:ASCII 码 0-31 的不可打印字符。
  • [[:digit:]]:匹配任意数字 0-9。
  • [[:graph:]]:A-Z、a-z、0-9 和标点符号。
  • [[:lower:]]:匹配任意小写字母 a-z。
  • [[:print:]]:ASCII 码 32-127 的可打印字符。
  • [[:punct:]]:标点符号(除了 A-Z、a-z、0-9 的可打印字符)。
  • [[:space:]]:空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)。
  • [[:upper:]]:匹配任意大写字母 A-Z。
  • [[:xdigit:]]:16进制字符(A-F、a-f、0-9)

有一种内网提权方式叫做通配符注入。了解部分通配符还是很有必要的。