Shell开发手册

Shell开发手册

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jV8nUJX-1606375818094)(Shell.assets/1602418402488.png)]

定义

作用:可以更方便的 使用 操作系统接口

工作原理: 负责把用户输入的字符串转换到需要执行的程序,并把结果以某个形式画出来的

列如: 在命令行里输入 “ls -Rl” 这种字符串。

  • 这个字符串被翻译成“ls”,“-R”,“-l”
  • “ls”帮我们找到那个之前写好的程序,并启动它;“-R”和“-l”被作为参数传给这个程序,告诉程序走“递归所有子目录”+“输出长格式”这部分代码

在这里插入图片描述

【bash】 + 【terminal】大概可以理解为一个以字符交互方式的 Shell

bash 负责按照某种格式把用户的输出的字符串翻译:

  • 比如对于普通非空字符翻译为程序和参数,并尝试去PATH里找对应的程序;
  • 对“空格”翻译成分隔符;对“$XXX”尝试进行环境变量的替换;
  • 对“|”翻译为管道;
  • 对 “>”翻译为输出重定向;
  • 对一个指令末尾的“&”翻译为将程序转到后台执行

参考: Shell 是用来解决什么问题的

hello world

# 新建shell文件
vim hello.sh
# 创建初,文件权限为:-rw-r--r--
# 并且文件后缀是否为 .sh 都可以,甚至不写后缀名都可以;建议最好加上,方便维护
# 编写内容
#!/bin/bash
echo 'Hello World'

#! 告诉系统这个脚本需要什么解释器来执行

/bin/bash 当我们没有指定解释器的时候默认的解释器

# 查看本机支持的解释器:
cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin

# 如果没有指定 #!/bin/bash
./hello.sh
# 将会默认使用 $SHELL 指定的解释器
echo $SHELL
/bin/bash
# 执行该文件
sh hello.sh

./hello.sh
# 若使用 ./ 方法执行文件,该文件需要有可执行文件的权限
chmod +x hello.sh

# 在Kali2020虚拟机上,将 #!/bin/bash 去掉,使用 ./ 方法也可以执行 ???

注释模板

# shebang
# 脚本的参数
# 脚本的用途
# 脚本的注意事项
# 脚本的写作时间,作者,版权等
# 各个函数前的说明注释
# 一些较复杂的单行命令注释

变量

Shell 变量 分为系统变量自定义变量

  • 系统变量有 H O M E 、 HOME、 HOMEPWD、$USER等
set 
# 显示当前shell所有变量

设置普通变量

# 等号两侧不能有空格,变量名一般习惯用大写
# 变量名=变量值
WILL=14

# 删除变量
unset WILL

# 声明静态变量,静态变量不能unset
readonly WILL

# 使用变量
echo $WILL

还有declare命令

设置环境变量

vim /etc/profile

# export 变量名=变量值,将 Shell 变量输出为环境变量
export JAVA_HOME=/usr/jdk1.8

# source 配置文件路径,让修改后的配置信息立即生效
source /etc/profile

# echo $变量名,检查环境变量是否生效
echo $JAVA_HOME

位置参数变量

  • $n :$0 代表命令本身、$1-$9 代表第1到9个参数,10以上参数用花括号,如 ${10}。
  • $* :命令行中所有参数,且把所有参数看成一个整体
  • $@ :命令行中所有参数,且把每个参数区分对待。
  • $# :所有参数个数。
vim positionPara.sh

#!/bin/bash     
# 输出各个参数 
echo $0 $1 $2 
echo $* 
echo $@ 
echo 参数个数=$#
sh positionPara.sh left right

# 执行后的结果:
positionPara.sh left right
left right
left right
2
  • $? : 在 Linux 下,不管你是启动一个桌面程序也好,还是在控制台下运行命令,所有的程序在结束时,都会返回一个数字值,这个值叫做返回值,或者称为错误号 ( Error Number )
ls *.sh
# ......
echo $?
# 0: 只要返回值是 0,就代表程序执行成功了

将上次命令执行是否成功的返回值放到提示符里面去:export PS1="[$?]${PS1}"

will@localhost ~ $ export PS1="[\$?]${PS1}"
[0]will@localhost ~ $

用perror查看错误提示

perror 2
# OS error code   2:  No such file or directory

预定义变量

在赋值定义之前,事先在 Shell 脚本中直接引用的变量

基本语法:

  • $$ :当前进程的 PID 进程号。
  • $! :后台运行的最后一个进程的 PID 进程号。
  • $? :最后一次执行的命令的返回状态,0为执行正确,非0执行失败。
#!/bin/bash

echo 当前的进程号=$$

# &:后台的方式运行进程
./ hello.sh &

echo 最后一个进程的进程号=$!
echo 最后执行的命令结果=$?
[root@izbp17osf716ivk4ilma92z test]# sh prePara.sh 
当前的进程号=4781
最后一个进程的进程号=4782
最后执行的命令结果=0

勤用双引号

使用”$”来获取变量的时候最好加上双引号

举一个例子:

#!/bin/sh
#已知当前文件夹有一个a.sh的文件
var="*.sh"
echo $var
echo "$var"
# 运行结果如下:
a.sh
*.sh

为啥会这样呢?其实可以解释为他执行了下面的命令:

echo *.sh
echo "*.sh"

参数规范

当脚本需要接受参数的时候,一定要先判断参数是否合乎规范,并给出合适的回显,方便使用者了解参数的使用

# 判断参数的个数
#!/bin/bash

if [ $# != 2 ]
then
    echo "Parameter incorrect."
    exit 1
fi

执行命令

# 反引号,执行里面的命令
test=`ls`
# 等价于反引号
test=$(ls)

尽量使用$()将命令的结果赋给变量,而不是反引号

运算符

  • $((运算式)) 或 $[运算式]
  • expr m + n
  • expr \*,/,% 分别代表乘,除,取余

注意 expr 运算符间要有空格

#!/bin/bash

# $(())
echo $(((4 + 7) * 3))

# $[]
echo $[(4 + 7) * 3]

# 使用expr
TEMP=`expr 4 + 7`
echo `expr $TEMP \* 3`

条件

vim guessGame.sh

#!/bin/bash

echo '你猜的数字为:'$1

if [ $1 -gt 7 ]
then
        echo '大于7'
elif [ $1 -lt 4 ]
then
        echo '小于4'
fi

注意:

[ condition ] : 前后都要有空格,非空返回0,0为true,否则为false

if [ : `if`距离条件方括号之间,需要有空格
# 是否存在文件/root/shell/isExist.txt 
if [ -e /root/shell/isExist.txt ] 
then
     echo '存在' 
fi  
if [ $1 = 7 ] && echo 'hello' || echo 'world' 
then
     echo '条件满足,执行后面的语句' 
fi

case

基本语法:

case $变量名 in
"值1")
如果变量值等于值1,则执行此处程序1
;;
"值2")
如果变量值等于值2,则执行此处程序2
;;
...省略其它分支...
*)
如果变量值不等于以上列出的值,则执行此处程序
;;
esac
case $1 in
"1")
echo 周一
;;
"2")
echo 周二
;;
*)
echo 其它
;;
esac

for

基本语法:

# 语法1
for 变量名 in 值1 值2 值3...
do
    程序
done

# 语法2
for ((初始值; 循环控制条件; 变量变化))
do
    程序
done

语法一

$*

#!/bin/bash  

# 使用$* 
for i in "$*" 
do     
    echo "the arg is $i" 
done 

# 输出:
thearg is 1 2 3 

$@

#!/bin/bash  

# 使用$@ 
for j in "$@" 
do     
    echo "the arg is $j" 
done

# 输出:
the arg is 1 
the arg is 2 
the arg is 3

语法二

#!/bin/bash 
SUM=0  
for ((i=1;i<=100;i++)) 
do     
    SUM=$[$SUM+$i] 
done 

echo $SUM
# 输出从1加到100的值

while

基本语法:

while [ 条件判断式 ]
do
    程序
done 
#!/bin/bash
SUM=0
i=0

while [ $i -le $1 ]
do
    SUM=$[$SUM+$i]
    i=$[$i+1]
done       
echo $SUM
# 输出从1加到100的值

awk

文本分析工具awk

awk [-F field-separator] 'commands' input-file(s)

实例:

awk -F : 'BEGIN {print "start1, start7"} {print $1 "," $7} END {print "end1, end7"}' /etc/passwd


# NF,浏览记录的字段个数 ; NR,已读的记录数; 
awk -F : '{ print NR  " "  NF  " "  $NF}' /etc/passwd  
1 7 /bin/bash
2 7 /sbin/nologin
3 7 /sbin/nologin
4 7 /sbin/nologin
LANG=c chage -l agentuser | grep 'Maximum number of days between password change' | 
							awk '{for(i=0; i<=9; i++) {print $i}}

读取控制台输入

基本语法:

read(选项)(参数)
  • 选项
    • -p:指定读取值时的提示符
    • -t:指定读取值时等待的时间(秒),如果没有在指定时间内输入,就不再等待
  • 参数
    • 变量名:读取值的变量名
vim guessGame.sh


函数

  • basename,删掉路径最后一个 / 前的所有部分(包括/),常用于获取文件名
  • basename [pathname] [suffx]
  • basename [string] [suffx]
  • 如果指定 suffx,也会删掉pathname或string的后缀部分
# basename /usr/bin/sort  
sort  

# basename include/stdio.h  
stdio.h  

# basename include/stdio.h .h 
stdio
  • dirname,删掉路径最后一个 / 后的所有部分(包括/),常用于获取文件路径
  • dirname pathname
  • 如果路径中不含 / ,则返回 ‘.’ (当前路径)
# dirname /usr/bin/  
/usr  

# dirname dir1/str dir2/str 
dir1 
dir2  

# dirname stdio.h 
.
# 表示当前路径

自定义函数

基本语法:

[ function ] funname[()]
{
    Action;
    [return int;]
}

# 调用
funname 参数1 参数2...
#!/bin/bash

function getSum(){
    SUM=$[$n1+$n2]
    echo "sum=$SUM"
}   

read -p "请输入第一个参数n1:" n1
read -p "请输入第二个参数n2:" n2

# 调用 getSum 函数
getSum $n1 $n2

巧用main函数

#!/bin/bash

func1(){
    #do sth
}
func2(){
    #do sth
}
main(){
    func1
    func2
}
main "$@"

func1 / func2 必须定义在调用main函数之前,可以定义在main函数之后

作用域

#!/bin/bash

var=1

func(){
   var=2
}
func

echo $var
# 2 : 输出结果就是2而不是1

间接引用

函数返回值

shell中函数的返回值只能是整数

也可以通过下面变通的方法: 通过echo或者print之类的就可以做到传一些额外参数的目的

#!/bin/bash

func(){
    echo "2333"
}
res=$(func)
echo "This is from $res."

heredocs

使用heredocs,可以非常方便的生成一些模板文件:

cat>>/etc/rsyncd.conf << EOF
log file = /usr/local/logs/rsyncd.log
transfer logging = yes
log format = %t %a %m %f %b
syslog facility = local3
EOF

查路径

pwd
# pwd获得的是当前shell的执行路径,而不是当前脚本的执行路径

# /home/will/test
# 正确的做法应该是下面这两种:
script_dir=$(cd $(dirname $0) && pwd)
script_dir=$(dirname $(readlink -f $0 ))

应当先cd进当前脚本的目录然后再pwd,或者直接读取当前脚本的所在路径。

命令并行化

shell中最简单的并行化是通过”&”以及”wait”命令来做

#!/bin/bash

func(){
    #do sthfor((i=0;i<10;i++))do
    func &
done
wait

这里并行的次数不能太多,否则机器会卡死。

如果图省事可以使用parallel命令来做,或者是用上面提到的xargs来处理。

-a到-z的意思

[ -a FILE ] 如果 FILE 存在则为真。
[ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真。
[ -c FILE ] 如果 FILE 存在且是一个字特殊文件则为真。
[ -d FILE ] 如果 FILE 存在且是一个目录则为真。
[ -e FILE ] 如果 FILE 存在则为真。
[ -f FILE ] 如果 FILE 存在且是一个普通文件则为真。
[ -g FILE ] 如果 FILE 存在且已经设置了SGID则为真。
[ -h FILE ] 如果 FILE 存在且是一个符号连接则为真。
[ -k FILE ] 如果 FILE 存在且已经设置了粘制位则为真。
[ -p FILE ] 如果 FILE 存在且是一个名字管道(F如果O)则为真。
[ -r FILE ] 如果 FILE 存在且是可读的则为真。
[ -s FILE ] 如果 FILE 存在且大小不为o则为真。
[ -t FD ] 如果文件描述符 FD 打开且指向一个终端则为真。
[ -u FILE ] 如果 FILE 存在且设置了SUID (set user ID)则为真。
[ -w FILE ] 如果 FILE 如果 FILE 存在且是可写的则为真。
[ -x FILE ] 如果 FILE 存在且是可执行的则为真。
[ -O FILE ] 如果 FILE 存在且属有效用户ID则为真。
[ -G FILE ] 如果 FILE 存在且属有效用户组则为真。
[ -L FILE ] 如果 FILE 存在且是一个符号连接则为真。
[ -N FILE ] 如果 FILE 存在 and has been mod如果ied since it was last read则为真。
[ -S FILE ] 如果 FILE 存在且是一个套接字则为真。
[ FILE1 -nt FILE2 ] 如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not则为真。
[ FILE1 -ot FILE2 ] 如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真。
[ FILE1 -ef FILE2 ] 如果 FILE1 和 FILE2 指向相同的设备和节点号则为真。
[ -o OPTIONNAME ] 如果 shell选项 “OPTIONNAME” 开启则为真。
[ -z STRING ] “STRING” 的长度为零则为真。
[ -n STRING ] or [ STRING ] “STRING” 的长度为非零 non-zero则为真。
[ STRING1 == STRING2 ] 如果2个字符串相同。 “=” may be used instead of “==for strict POSIX compliance则为真。
[ STRING1 != STRING2 ] 如果字符串不相等则为真。
[ STRING1 < STRING2 ] 如果 “STRING1” sorts before “STRING2” lexicographically in the current locale则为真。
[ STRING1 > STRING2 ] 如果 “STRING1” sorts after “STRING2” lexicographically in the current locale则为真。

[ -z “echo 111s|sed 's/[0-9]//g'] && echo 1 || echo 0 #把字符串中的数字都替换掉

正则表达式

[[ $test =~ ^[0-9]+ ]] && echo 1 || echo 0

~ 其实是对后面的正则表达式表示匹配的意思,如果匹配就输出1, 不匹配就输出0

比较符

*-eq(equal)* : 测试两个整数是否相等;比如 $A -eq $B

*-ne**(**inequality********)* : 测试两个整数是否不等;不等,为真;相等,为假;

*-gt(greter than)* : 测试一个数是否大于另一个数;大于,为真;否则,为假;

*-lt********(less than)* : 测试一个数是否小于另一个数;小于,为真;否则,为假;

*-ge**(greter equal)***: 大于或等于

*-le********(less equal)* :小于或等于

常用命令

# 获取当前目录下,所有文件和文件夹的名字,一行一行的
ls -1a
# 查看当前的进程的网络
netstat -an | grep 进程号

# 查看当前进程号对应的 PID
lsof -l :进程号

kill -9 PID
# sha1 文件校验
sha1sum 文件名
# 在 “文件夹范围内” 两天内修改的文件/文件夹
find 范围 -mtime -2

Linux-文件搜索命令find的使用

高效命令

  • 如果你想要有语法高亮的 cat,可以试试 ccat 命令。
  • exa 增强了 ls 命令,如果你需要在很多目录上浏览各种文件 ,ranger 命令可以比 cdcat 更有效率,甚至可以在你的终端预览图片。
  • fd 是一个比 find 更简单更快的命令,他还会自动地忽略掉一些你配置在 .gitignore 中的文件,以及 .git 下的文件。
  • fzf 会是一个很好用的文件搜索神器,其主要是搜索当前目录以下的文件,还可以使用 fzf --preview 'cat {}'边搜索文件边浏览内容。
  • grep 是一个上古神器,然而,ackagrg 是更好的grep,和上面的 fd一样,在递归目录匹配的时候,会使用你配置在 .gitignore 中的规则。
  • rm 是一个危险的命令,尤其是各种 rm -rf …,所以,trash 是一个更好的删除命令。
  • man 命令是好读文档的命令,但是man的文档有时候太长了,所以,你可以试试 tldr 命令,把文档上的一些示例整出来给你看。
  • 如果你想要一个图示化的ping,你可以试试 prettyping
  • 如果你想搜索以前打过的命令,不要再用 Ctrl +R 了,你可以使用加强版的 hstr
  • htop 是 top 的一个加强版。然而,还有很多的各式各样的top,比如:用于看IO负载的 iotop,网络负载的 iftop, 以及把这些top都集成在一起的 atop
  • ncdu 比 du 好用多了。另一个选择是 nnn
  • 如果你想把你的命令行操作录制成一个 SVG 动图,那么你可以尝试使用 asciinemasvg-trem
  • httpie 是一个可以用来替代 curlwget 的 http 客户端,httpie 支持 json 和语法高亮,可以使用简单的语法进行 http 访问: http -v github.com
  • tmux 在需要经常登录远程服务器工作的时候会很有用,可以保持远程登录的会话,还可以在一个窗口中查看多个 shell 的状态。
  • sshrc 是个神器,在你登录远程服务器的时候也能使用本机的 shell 的 rc 文件中的配置。
  • goaccess 这个是一个轻量级的分析统计日志文件的工具,主要是分析各种各样的 access log。

参考:

shell基本语法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值