Shell是一个用C语言编写的程序,它是用户使用linux的桥梁,Shell既是一种命令语言,又是一种程序设计语言。
Shell语法与php类似,容易上手
感觉这个语言蛮有意思的,在linux上写一些自动化脚本挺不错的。结合了菜鸟教程和网上视频简单学了学。
#! /bin/bash echo "this is my first shell program!"
result:this is my first shell program!
#!
用来告诉系统这个脚本需要什么解释器来执行,即使用哪一种shell,如果是python脚本,开头可以写上#! /usr/bin/python
,即表示用python解释器来执行运行Shell脚本的方法:
将上述代码保存为hello.sh(文本后缀对linux来说没有太大意义,其实该后缀可以任意修改,仍然可以正常运行),通过命令行cd到相应的目录,为该脚本赋予可执行权限后,通过./hello.sh的方式即可执行脚本
chmod +x hello.sh ./hello.sh
需要注意的是:
- 只能通过./test.sh去运行,而不能通过文件名test.sh去运行。直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,当前的目录通常不会在PATH里,所以直接输入test.sh 会显示找不到命令,要用./test.sh告诉系统,就在当前目录找。
- 该方法会打开一个子shell读取并执行hello.sh中的命令
直接输入hello.sh的结果:
┌──(root㉿kali)-[/home/kali/桌面] └─# hello.sh hello.sh: command not found
当然如果要让系统找到该文件,也可以直接输入绝对路径,这样也能成功执行脚本,例如:
┌──(root㉿kali)-[/home/kali/桌面] └─# /home/kali/桌面/hello.sh this is my first shell program!
作为解释器参数
直接运行解释器,作用的参数为shell脚本名,文件可以无执行权限。例如:
/bin/sh hello.sh # 运行shell脚本 /usr/bin/python hello.py # 运行python脚本
该方法会打开一个子shell来读取并执行hello.sh中的命令
使用source hello.sh
或者. hello.sh
(bash特有)方式,其作用是在当前shell环境下读取并执行hello.sh脚本中的命令,文件可以无执行权限。
cat /etc/shells,可以查看当前系统可以使用哪些shell
echo $SHELL 可以查看自己当前使用的是什么shell
以下将使用kali中的bash命令行作为示例(kali默认使用的shell是zsh,在命令行中输入bash即可进入使用bash的子shell进程中
以下内容仅适用于适用bash的命令行
Shell会保留其会话中用户提交执行的命令
history -c 清空内存中命令历史 -r 从文件中恢复历史命令 数字 :显示最近n条命令 例如: history 10 echo $HISTSIZE # 显示保留历史的条数 # 存放用户执行的历史命令,写入文件 ┌──(root㉿kali)-[/home/kali] └─# echo $HISTFILE /root/.bash_history # kali使用的是zsh ,所以保存路径为 /root/.zsh_history # 调用历史记录 ![历史id] 可以快速执行历史命令 !! 执行上次历史命令
以下赋值方式都是可以的
name="123" name=123 name='123'
shell默认把所有变量都认为是字符串
shell变量是弱类型,无需事先声明类型,是将声明和赋值同时进行变量替换/引用
┌──(root㉿kali)-[/home/kali] └─# name="123" ┌──(root㉿kali)-[/home/kali] └─# echo $name # 可以在变量名前加$符号 123 ┌──(root㉿kali)-[/home/kali] └─# echo ${name} # 比较完整的是前面加$,变量名再用{}包裹,加花括号是为了帮助解释器识别变量的边界 123
'x',"x",x,`x`
- 单引号:所见即所得,是强引用
- 双引号:输出引号里所有的内容,识别特殊符号,弱引用
- 无引号:连续的符号可以不加引号,有空格则有歧义,最好使用双引号
- 反引号,引用命令执行结果,等于$()用法
┌──(kali㉿kali)-[~/桌面] └─$ num1=1 ┌──(kali㉿kali)-[~/桌面] └─$ num2=2 ┌──(kali㉿kali)-[~/桌面] └─$ num3="$num1" ┌──(kali㉿kali)-[~/桌面] └─$ echo $num3 1 ┌──(kali㉿kali)-[~/桌面] └─$ num4='$num2' ┌──(kali㉿kali)-[~/桌面] └─$ echo $num4 $num2
`linux命令` # echo 这个值会直接运行该linux命令 ┌──(root㉿kali)-[/home/kali/桌面] └─# name=`whoami` ┌──(root㉿kali)-[/home/kali/桌面] └─# echo $name root
变量名规则
不得引用保留关键字(help查看关键字)
只能包含数字、字母、下划线
不能以数字开头
不能用标点符号
变量名严格区分大小写
变量作用域
可以发现在当前shell中定义的变量在子shell(在当前shell中输入想要使用的shell名字,比如zsh,bash,sh,即可进入子shell中无法引用,同样的,子shell中定义的变量也无法在当前shell中引用,因为作用域的缘故。
每次调用bash/sh解释器执行脚本,都会开启一个子shell,因此不保留脚本运行过程中的shell变量
通过pstree命令可以检查进程树,查看当前使用的shell的父子情况
调用source或者. 等方式执行脚本是在当前shell环境加载脚本,因此保留变量
环境变量一般指的是用export
内置命令导出的变量,用于定义shell的运行环境、保证shell命令的正确执行。shell通过环境变量确定登录的用户名,PATH路径、文件系统等各种应用。
环境变量可以在命令行中临时创建,但是用户退出shell终端,变量即丢失,如要永久生效,需要修改环境变量配置文件
。
~/.bash_profile
、~/.bashrc 远程登录用户特有文件
/etc/profile
,/etc/bashrc
,且系统建议最好创建在/etc/profile.d/
,而非直接修改主文件,修改全局配置文件,影响所有登录系统的用户~/.bash_profile
和.bashrc
,每个用户中的环境变量只在自己的shell终端中生效,每次写入新的环境变量后,需重新登录才能加载新写入的环境变量/etc/profile
检查系统环境变量的命令
命令 | 作用 |
---|---|
set | 输出所有变量,包括全局变量、局部变量 |
env | 只显示全局变量 |
declare | 输出所有的变量,如同set |
export | 显示和设置环境变量值 |
撤销环境变量
设置只读变量
┌──(root㉿kali)-[/home/kali/桌面] └─# readonly passwd=123 ┌──(root㉿kali)-[/home/kali/桌面] └─# echo $passwd 123 ┌──(root㉿kali)-[/home/kali/桌面] └─# passwd=234 bash: passwd:只读变量
系统保留环境变量关键字
bash内嵌了诸多环境变量,用于定义bash的工作环境
通过下面命令可以查看:
export |awk -F '[ :=]' '{print $3}' >env cat env
result:
COLORTERM COMMAND_NOT_FOUND_INSTALL_PROMPT DISPLAY DOTNET_CLI_TELEMETRY_OPTOUT HOME LANG LANGUAGE LESS_TERMCAP_mb LESS_TERMCAP_md LESS_TERMCAP_me LESS_TERMCAP_se LESS_TERMCAP_so LESS_TERMCAP_ue LESS_TERMCAP_us LOGNAME LS_COLORS MAIL OLDPWD PATH POWERSHELL_TELEMETRY_OPTOUT POWERSHELL_UPDATECHECK PWD SHELL SHLVL SUDO_COMMAND SUDO_GID SUDO_UID SUDO_USER TERM USER XAUTHORITY
在命令行中输入
man sh
,查找Special Parameters,即可看到部分特殊变量
参数处理 | 说明 |
---|---|
$0 | 获取shell脚本文件名,以及脚本路径 |
$n | 获取shell脚本传递的第n个参数,n在1-9之间,如$1、$2、$9,大于则需要写,${10},参数空格隔开 |
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 |
$_ | 在此之前执行的命令的最后一个参数 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值(1-255)表明有错误。 |
运行如下特殊变量.sh文件:
┌──(root㉿kali)-[/home/kali/桌面] └─# cat 特殊变量.sh #! /bin/bash echo -e "\n" # 使用-e参数可以输出转义字符 echo '$n' echo "文件名为:$0" echo "第一个参数:$1"第二个参数:$2"第三个参数:$3" echo -e "\n" echo '$#' echo "参数个数为:$#" echo -e "\n" echo '$$' echo "该文件运行的PID为$$" echo -e "\n" echo '$*' echo "所有参数为:$*" echo -e "\n" echo '$@' echo "所有参数为:$@"
结果:
┌──(root㉿kali)-[/home/kali/桌面] └─# ./特殊变量.sh a b c $n 文件名为:./参数传递.sh 第一个参数:a第二个参数:b第三个参数:c $# 参数个数为:3 $$ 该文件运行的PID为40241 $* 所有参数为:a b c $@ 所有参数为:a b c
$_
运行上述脚本后,输出$_
┌──(root㉿kali)-[/home/kali/桌面] └─# echo $_ c
$!
┌──(root㉿kali)-[/home/kali/桌面] └─# nohup ping www.baidu.com & 1>/dev/hull [1] 49488 ┌──(root㉿kali)-[/home/kali/桌面] └─# nohup: 忽略输入并把输出追加到'nohup.out' echo $! 49488 ┌────(root㉿kali)-[/home/kali/桌面] └─# kill -9 $! ┌────(root㉿kali)-[/home/kali/桌面] └─# [1]+ 已杀死 nohup ping www.baidu.com
$*与$@的区别
当 $* 和 $@ 不被双引号 " " 包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。
但是当它们被双引号" "包含时,就会有区别了:
- "$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据;
- "$@"仍然将每个参数都看作一份数据,彼此之间是独立的。
比如传递了 5 个参数,那么对于 $* 来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;而对于 $@ 来说,这 5 个参数是相互独立的,它们是 5 份数据。
如果使用 echo 直接输出 $* 和 $@ 做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。
运行如下test.sh文件
┌──(root㉿kali)-[/home/kali/桌面] └─# cat test.sh #! /bin/bash echo "params from $* " for _ in "$*";do echo $_ done echo "params from $@ " for _ in "$@";do echo $_ done
结果:
┌──(root㉿kali)-[/home/kali/桌面] └─# ./test.sh a b c params from a b c a b c params from a b c a b c