作用:可以更方便的 使用 操作系统 的
接口
工作原理: 负责把用户输入的字符串转换到需要执行的程序,并把结果以某个形式画出来的
列如: 在命令行里输入 “ls -Rl” 这种字符串。
- 这个字符串被翻译成“ls”,“-R”,“-l”
- “ls”帮我们找到那个之前写好的程序,并启动它;“-R”和“-l”被作为参数传给这个程序,告诉程序走“递归所有子目录”+“输出长格式”这部分代码
【bash】 + 【terminal】大概可以理解为一个以
字符
为交互方式的 Shell
bash 负责按照某种格式把用户的输出的字符串翻译:
参考: Shell 是用来解决什么问题的
# 新建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、 HOME、PWD、$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
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
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 脚本中直接引用的变量
基本语法:
#!/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 运算符间要有空格
#!/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 $变量名 in
"值1")
如果变量值等于值1,则执行此处程序1
;;
"值2")
如果变量值等于值2,则执行此处程序2
;;
...省略其它分支...
*)
如果变量值不等于以上列出的值,则执行此处程序
;;
esac
case $1 in
"1")
echo 周一
;;
"2")
echo 周二
;;
*)
echo 其它
;;
esac
基本语法:
# 语法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 [ 条件判断式 ]
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 [-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(选项)(参数)
vim guessGame.sh
# basename /usr/bin/sort
sort
# basename include/stdio.h
stdio.h
# basename include/stdio.h .h
stdio
# 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
#!/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,可以非常方便的生成一些模板文件:
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 sth
}
for((i=0;i<10;i++))do
func &
done
wait
这里并行的次数不能太多,否则机器会卡死。
如果图省事可以使用parallel命令来做,或者是用上面提到的xargs来处理。
[ -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 命令。ls
命令,如果你需要在很多目录上浏览各种文件 ,ranger 命令可以比 cd
和 cat
更有效率,甚至可以在你的终端预览图片。find
更简单更快的命令,他还会自动地忽略掉一些你配置在 .gitignore
中的文件,以及 .git
下的文件。fzf --preview 'cat {}'
边搜索文件边浏览内容。grep
是一个上古神器,然而,ack、ag 和 rg 是更好的grep,和上面的 fd
一样,在递归目录匹配的时候,会使用你配置在 .gitignore
中的规则。rm
是一个危险的命令,尤其是各种 rm -rf …
,所以,trash 是一个更好的删除命令。man
命令是好读文档的命令,但是man的文档有时候太长了,所以,你可以试试 tldr 命令,把文档上的一些示例整出来给你看。ping
,你可以试试 prettyping 。curl
和 wget
的 http 客户端,httpie
支持 json 和语法高亮,可以使用简单的语法进行 http 访问: http -v github.com
。参考:
shell基本语法