- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
# 自定义vim,以便于更好的编写shell
[root@kylin-xu ~]# vim .vimrc
set ts=4
set ai
autocmd BufNewFile *.sh exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1,"#!/bin/bash")
call setline(2,"#")
call setline(3,"#****************************************************")
call setline(4,"#Author: xu")
call setline(5,"#QQ: 123456")
call setline(6,"#Date: ".strftime("%Y-%m-%d"))
call setline(7,"#FileName: ".expand("%"))
call setline(8,"#URL: https://www.cnblogs.com/xuruizhao")
call setline(9,"#Description: test")
call setline(10,"#Copyright(C): ".strftime("%Y")." All right")
call setline(11,"#***************************************************")
call setline(12,"")
call setline(13,"")
call setline(14,"")
endif
endfunc
autocmd BufNewFile * normal G
shell是基于过程式、解释执行的语言.
shell脚本是包含一些命令或声明,并符合一定格式的文本文件.
一个shell脚本文件中主要包含以下内容 。
格式要求:首行shebang机制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
#!/usr/bin/ruby
#!/usr/bin/lua
用编辑器创建新文件,首行必须是 shell 声明(shebang),编辑完成后保存退出 。
添加可执行权限 。
运行脚本 。
#新建文件
[root@kylin-xu ~]# vim test.sh
#!/bin/bash
echo "hello world"
# 如果没有x权限,在执行时就需要加上解释器
[root@kylin-xu ~]# bash test.sh
hello world
#加可执行权限,通过绝对路径和相对路径执行
[root@kylin-xu ~]# ./test.sh
-bash: ./test.sh: 权限不够
[root@kylin-xu ~]# chmod +x test.sh
[root@kylin-xu ~]# ./test.sh
hello
# 我们也可以将脚本路径加到PATH中,然后就可以直接执行脚本了
[root@kylin-xu ~]# test.sh
-bash: test.sh:未找到命令
[root@kylin-xu ~]# PATH=`pwd`:$PATH
[root@kylin-xu ~]# echo $PATH
/root:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin
[root@kylin-xu ~]# test.sh
hello world
[root@kylin-xu scripts]# vim hello.sh
#!/bin/bash
echo hello world
echo "hello world"
echo 'hello world'
[root@kylin-xu scripts]# bash hello.sh
hello world
hello world
hello world
当前shell执行,和子shell执行 。
# 父子shell的关系
root@xu-ubuntu:~/scripts# cat 01.sh
#!/bin/bash
echo hello world
echo 'hello world'
echo "hello world"
sleep 100
# bash 01.sh是在当前shell执行
root@xu-ubuntu:~/scripts# bash 01.sh
hello world
hello world
hello world
root@xu-ubuntu:~# pstree -p
├─sshd(29225)─┬─sshd(83993)───sshd(84113)───bash(84114)───su(84241)───bash(84256)───bash(84456)───sleep(84457)
# ./01.sh 是在子shell执行
root@xu-ubuntu:~/scripts# ./01.sh
hello world
hello world
hello world
root@xu-ubuntu:~# pstree -p
├─sshd(29225)─┬─sshd(83993)───sshd(84113)───bash(84114)───su(84241)───bash(84256)───01.sh(84468)───sleep(84469)
本地执行远程脚本 - 脚本文件在远程主机,在本机执行并在本机生效,执行结果影响的是本机 。
test.sh脚本在192.168.121.99主机上
[root@kylin-xu html]# cat /var/www/html/test.sh
#!/bin/bash
echo 'haha'
touch /root/1.txt
在192.168.121.88上执行并生效
root@xu-ubuntu:~/scripts# curl http://192.168.121.99/test.sh | bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 375 100 375 0 0 72032 0 --:--:-- --:--:-- --:--:-- 93750
haha
root@xu-ubuntu:~/scripts# ll /root/1.txt
-rw-r--r-- 1 root root 0 Nov 24 19:24 /root/1.txt
脚本在本地,我在远程主机执行本地脚本,脚本的输出结果是在本地,但是脚本的作用对象是远程主机 。
脚本在本地,在本地执行
root@xu-ubuntu:~/scripts# vim 02.sh
#!/bin/bash
hostname
hostname -I
root@xu-ubuntu:~/scripts# bash 02.sh
xu-ubuntu
192.168.121.88
# 连接上远程主机,执行本地shell,shell的结果输出在本地,但是却作用于远程主机
root@xu-ubuntu:~/scripts# ssh 192.168.121.99 /bin/bash < 02.sh
kylin-xu
192.168.121.99
写一个脚本,获取主机系统信息,包括CPU型号,内存大小,硬盘大小,操作系统版本这四个指标 。
# version1.0
root@xu-ubuntu:~/scripts# vim sys_ver1.0.sh
#! /bin/bash
lscpu | grep 'Model name' | sed -r 's#.*:[ ]+(.*$)#\1#'
free -h | awk 'NR==2{print $2}'
lsblk | awk '/^sda/{print $(NF-2)}'
grep 'PRETTY_NAME' /etc/os-release | sed -r 's#.*"(.*)"$#\1#'
root@xu-ubuntu:~/scripts# chmod +x sys_ver1.0.sh
root@xu-ubuntu:~/scripts# ./sys_ver1.0.sh
13th Gen Intel(R) Core(TM) i5-13500HX
3.8Gi
50G
Ubuntu 22.04.5 LTS
# version2
root@xu-ubuntu:~/scripts# vim sys_ver2.0.sh
#!/bin/bash
echo -e "CPU info: \c"
lscpu | grep 'Model name' | sed -r 's#.*:[ ]+(.*$)#\1#'
echo -e "free total: \c"
free -h | awk 'NR==2{print $2}'
echo -e "hd size: \c"
lsblk | awk '/^sda/{print $(NF-2)}'
echo -e "sys info: \c"
grep 'PRETTY_NAME' /etc/os-release | sed -r 's#.*"(.*)"$#\1#'
root@xu-ubuntu:~/scripts# bash sys_ver2.0.sh
CPU info: 13th Gen Intel(R) Core(TM) i5-13500HX
free total: 3.8Gi
hd size: 50G
sys info: Ubuntu 22.04.5 LTS
# version3,给输出加上颜色
root@xu-ubuntu:~/scripts# vim sys_ver2.0.sh
#!/bin/bash
echo -e "CPU info: \c"
echo -e "\E[1;32m`lscpu | grep 'Model name' | sed -r 's#.*:[ ]+(.*$)#\1#'`\E[0m"
echo -e "free total: \c"
echo -e "\E[1;32m`free -h | awk 'NR==2{print $2}'`\E[0m"
echo -e "hd size: \c"
echo -e "\E[1;32m`lsblk | awk '/^sda/{print $(NF-2)}'`\E[0m"
echo -e "sys info: \c"
echo -e "\E[1;32m`grep 'PRETTY_NAME' /etc/os-release | sed -r 's#.*"(.*)"$#\1#'`\E[0m"
root@xu-ubuntu:~/scripts# bash sys_ver2.0.sh
CPU info: 13th Gen Intel(R) Core(TM) i5-13500HX
free total: 3.8Gi
hd size: 50G
sys info: Ubuntu 22.04.5 LTS
执行前先做语法检查 。
此选项只能检测脚本中的语法错误,不能检测命令错误,也不会执行脚本 。
bash -n
root@xu-ubuntu:~/scripts# vim 03.sh
#!/bin/bash
echo 123
echo 456
echo "ased
root@xu-ubuntu:~/scripts# bash -n 03.sh
03.sh: line 16: unexpected EOF while looking for matching `"'
03.sh: line 17: syntax error: unexpected end of file
调试并执行,查找命令的错误 。
逐行输出命令,并输出执行结果 。
bash -x
root@xu-ubuntu:~/scripts# vim 04.sh
echo '123'
id
hostname
hostname -I
echo 45
root@xu-ubuntu:~/scripts# bash -x 04.sh
+ echo 123
123
+ id
uid=0(root) gid=0(root) groups=0(root)
+ hostname
xu-ubuntu
+ hostname -I
192.168.121.88
+ echo 456
456
# 获取两天后的日期
root@xu-ubuntu:~/scripts# date +%F
2024-11-24
root@xu-ubuntu:~/scripts# date +%F --date="+2 day"
2024-11-26
总结:脚本错误常见的有三种 。
变量类型:
不同的变量存放的数据不同,决定了以下 。
变量数据类型:
命名要求 。
命名习惯 。
变量的生效范围等标准划分变量类型 。
name='value'
value 可以是以下多种形式 。
name='root' #直接字串
name="$USER" #变量引用
name=`COMMAND` #命令引用
name=$(COMMAND) #命令引用
root@xu-ubuntu:~/scripts# name="tom"
root@xu-ubuntu:~/scripts# echo $name
tom
root@xu-ubuntu:~/scripts# name=$USER
root@xu-ubuntu:~/scripts# echo $name
root
root@xu-ubuntu:~/scripts# name=`hostname`
root@xu-ubuntu:~/scripts# echo $name
xu-ubuntu
root@xu-ubuntu:~/scripts# name=$(hostname)
root@xu-ubuntu:~/scripts# echo $name
xu-ubuntu
注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚本结束,也会自动删除 。
$name
${name}
弱引用和强引用 。
变量的间接赋值和引用 。
root@xu-ubuntu:~/scripts# Ti=cto
root@xu-ubuntu:~/scripts#
root@xu-ubuntu:~/scripts# name=wang
root@xu-ubuntu:~/scripts# Ti=$name
root@xu-ubuntu:~/scripts# echo $Ti
wang
root@xu-ubuntu:~/scripts# name=xu
root@xu-ubuntu:~/scripts# echo $Ti
wang
变量追加值 。
root@xu-ubuntu:~# TI=cto
root@xu-ubuntu:~# TI+=:xu
root@xu-ubuntu:~# echo $TI
cto:xu
在脚本中使用变量 。
COLOR=33
echo -e "CPU info: \c"
echo -e "\E[1;${COLOR}m`lscpu | grep 'Model name' | sed -r 's#.*:[ ]+(.*$)#\1#'`\E[0m"
echo -e "free total: \c"
echo -e "\E[1;${COLOR}m`free -h | awk 'NR==2{print $2}'`\E[0m"
echo -e "hd size: \c"
echo -e "\E[1;${COLOR}m`lsblk | awk '/^sda/{print $(NF-2)}'`\E[0m"
echo -e "sys info: \c"
echo -e "\E[1;${COLOR}m`grep 'PRETTY_NAME' /etc/os-release | sed -r 's#.*"(.*)"$#\1#'`\E[0m"
root@xu-ubuntu:~/scripts# bash sys_ver2.0.sh
CPU info: 13th Gen Intel(R) Core(TM) i5-13500HX
free total: 3.8Gi
hd size: 50G
sys info: Ubuntu 22.04.5
root@xu-ubuntu:~/scripts# name=xixi
root@xu-ubuntu:~/scripts# echo $name
xixi
root@xu-ubuntu:~/scripts# unset name
root@xu-ubuntu:~/scripts# echo $name
root@xu-ubuntu:~/scripts# set | grep name=
name=xixi
环境变量:
变量声明和赋值:
#声明并赋值
export name=VALUE
declare -x name=VALUE
#或者分两步实现
name=VALUE
export name
显示所有环境变量 。
env
printenv
export
declare -x
查看指定进程的环境变量 。
cat /proc/$PID/environ
删除变量 。
unset
bash内建的环境变量 。
PATH
SHELL
USER
UID
HOME
PWD
SHLVL #shell的嵌套层数,即深度
LANG
MAIL
HOSTNAME
HISTSIZE
_ #下划线,表示前一命令的最后一个参数
root@xu-ubuntu:~# var1=123;export var1
root@xu-ubuntu:~# var2=456
# var1是环境变量,var2是普通变量
root@xu-ubuntu:~# vim scripts/en.sh
echo $var1
echo $var2
echo $PS1
echo $PATH
root@xu-ubuntu:~# ./scripts/en.sh # 子shell执行
123
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
在父shell中定义的环境变量,在子shell中同样也可以引用 。
只读变量:只能声明定义,但后续不能修改和删除,即常量 。
声明只读变量 。
readonly name
declare -r name
查看只读变量 。
readonly [-p]
declare -r
root@xu-ubuntu:~# readonly name
root@xu-ubuntu:~# name="asd"
-bash: name: readonly variable
root@xu-ubuntu:~# readonly name="aaa"
root@xu-ubuntu:~# echo $name
aaa
root@xu-ubuntu:~# name=haha
-bash: name: readonly variable
位置变量:在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数 。
$1,$2,...,$9,${10} #对应第1个、第2个等参数,shift [n]换位置
$0 #shell脚本的文件名
$* #传递给脚本的所有参数,全部参数合为一个字符串
$@ #传递给脚本的所有参数,每个参数为独立字符串
$# #传递给脚本的参数的个数
$$ #输出当前的PID
root@xu-ubuntu:~# vim scripts/arg.sh
echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "11st arg is ${11}"
echo "The number of arg is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@ubuntu2204 ~]# bash /data/scripts/arg.sh {a..z}
1st arg is a
2st arg is b
3st arg is c
10st arg is j
11st arg is k
The number of arg is 26
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
The scriptname is arg.sh
$*和$@的区别 。
for i in "$*";do
echo $i
done
for j in "$@";do
echo $j
done
root@xu-ubuntu:~# bash scripts/05.sh 1 2 3
1 2 3
1
2
3
$*:将接收到的参数看为一个整体 。
$@:将接收到每一个参数看作一个单独的参数 。
范例: 利用软链接实现同一个脚本不同功能 。
root@xu-ubuntu:~# ll /usr/sbin/shutdown
lrwxrwxrwx 1 root root 14 Nov 22 2023 /usr/sbin/shutdown -> /bin/systemctl*
root@xu-ubuntu:~# ll /usr/sbin/reboot
lrwxrwxrwx 1 root root 14 Nov 22 2023 /usr/sbin/reboot -> /bin/systemctl*
root@xu-ubuntu:~/scripts# cat test.sh
echo $0
root@xu-ubuntu:~/scripts# ln -s test.sh a.sh
root@xu-ubuntu:~/scripts# bash a.sh
a.sh
root@xu-ubuntu:~/scripts# ln -s test.sh b.sh
root@xu-ubuntu:~/scripts# bash b.sh
b.sh
if [ $0 == "a.sh" ];then
echo "aaa"
elif [ $0 == "b.sh" ];then
echo "bbb"
fi
root@xu-ubuntu:~/scripts# bash a.sh
aaa
root@xu-ubuntu:~/scripts# bash b.sh
bbb
进程执行后,将使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败,$?取值范例 0-255 。
$? #0代表成功,1-255代表失败
root@xu-ubuntu:~/scripts# echo $?
0
root@xu-ubuntu:~/scripts# ping -c1 -w1 baidu.afasfsascom >/dev/null 2>&1
root@xu-ubuntu:~/scripts# echo $?
2
用户可以在脚本中使用以下命令自定义退出状态码 。
exit [n]
注意:
root@xu-ubuntu:~/scripts# echo $-
himBHs
选项 | 含义 |
---|---|
h | 开启hash表缓存外部命令路径 |
i | 当前是交互shell |
m | 开启监控模式,可通过 Job control来控制进程的停止、继续,后台 |
B | 是否支持大括号扩展 |
H | 是否可用 ! 快速展开history命令 |
root@xu-ubuntu:~/scripts# echo $-
himBHs
root@xu-ubuntu:~/scripts# set +B
root@xu-ubuntu:~/scripts# echo $-
himHs
root@xu-ubuntu:~/scripts# echo {1..4}
{1..4}
root@xu-ubuntu:~/scripts# set -B
root@xu-ubuntu:~/scripts# echo $-
himBHs
root@xu-ubuntu:~/scripts# set +h
root@xu-ubuntu:~/scripts# hash
-bash: hash: hashing disabled
终端中的$- 和 shell脚本中的$- 不同 。
root@xu-ubuntu:~/scripts# cat 07.sh
echo $-
root@xu-ubuntu:~/scripts# bash 07.sh
hB
root@xu-ubuntu:~/scripts# echo $-
himBHs
相当于增强版的 echo, 实现丰富的格式化输出 。
格式 。
printf format args..
常用格式替换符 。
替换符 | 功能 |
---|---|
%s | 字符串 |
%d,%i | 十进制整数 |
%f | 浮点类型 |
%c | ASCII字符,即显示对应参数的第一个字符 |
%b | 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义 |
%o | 八进制 |
%u | 无符号数 |
%x | 十六进制数值(a-f) |
%X | 十六进制数值(A-F) |
%% | 表示%本身 |
常用转义字符 。
转义符 | 功能 |
---|---|
\a | 警告字符通常为ASCII的BEL字符 |
\b | 退格 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ | 表示 \ 本身 |
root@xu-ubuntu:~/scripts# printf "%s\n" 1 2 3
1
2
3
root@xu-ubuntu:~/scripts# printf "%f\n" 1 2
1.000000
2.000000
# .3f表示保存3位小数
root@xu-ubuntu:~/scripts# printf "%.3f\n" 1.1234 2.12345
1.123
2.123
# 最后的echo表示换行的意思
root@xu-ubuntu:~/scripts# printf "(%s)---" 1 2 3 ;echo
(1)---(2)---(3)---
root@xu-ubuntu:~/scripts# printf "%-10s %-10s %-4s %s \n" 姓名 性别 年龄 体重 小明 男 20 70 小红 女 18 50
# - 表示左对齐,不加- 是右对齐
姓名 性别 年龄 体重
小明 男 20 70
小红 女 18 50
# 进制转换
14root@xu-ubuntu:~/scripts# printf "%o" 12;echo
14
0root@xu-ubuntu:~/scripts# printf "%x" 999 ;echo
3e7
root@xu-ubuntu:~/scripts# printf "%X" 999 ;echo
3E7
Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩展。求值以固定宽度的整数进行,不检查溢出,尽管除以0 被标记为错误。运算符及其优先级,关联性和值与C语言相同。以下运算符列表分组为等优先级运算符级别。级别按降序排列优先.
注意:bash 只支持整数,不支持小数 。
+ - #addition, subtraction
* / % #multiplication, division, remainder, %表示取模,即取余数,示例:9%4=1,5%3=2
i++ i-- #variable post-increment and post-decrement
++i --i #variable pre-increment and pre-decrement
= *= /= %= += -= <<= >>= &= ^= |= #assignment
- + #unary minus and plus
! ~ #logical and bitwise negation
** #exponentiation 乘方,即指数运算
<< >> #left and right bitwise shifts
<= >= < > #comparison
== != #equality and inequality
& #bitwise AND
| #bitwise OR
^ #bitwise exclusive OR
&& #logical AND
|| #logical OR
expr?expr:expr #conditional operator
expr1 , expr2 #comma
乘法符号有些场景中需要转义 。
实现算术运算 。
let var=expression
((var=expression))
var=$[expression]
var=$((expression))
var=$(expr arg1 arg2 ...)
declare -i var=number
echo expression|bc
# 如果不进行转义是无法进行运算的
root@xu-ubuntu:~/scripts# i=3
root@xu-ubuntu:~/scripts# j=4
root@xu-ubuntu:~/scripts# echo i+j
i+j
# 进行转义
root@xu-ubuntu:~/scripts# echo $[i+j]
7
root@xu-ubuntu:~/scripts# echo $((i+j))
7
root@xu-ubuntu:~/scripts# let k=i+j
root@xu-ubuntu:~/scripts# echo $k
7
# expr进行算数。在进行乘法运算时要进行转义,而且必须要有空格
root@xu-ubuntu:~/scripts# expr 3 + 4
7
root@xu-ubuntu:~/scripts# expr 3 \* 4
12
范例:生成 0 - 49 之间随机数 。
$RANDOM 随机数的范围 0-32767
root@xu-ubuntu:~/scripts# echo $[RANDOM%50]
33
root@xu-ubuntu:~/scripts# echo $[RANDOM%50]
5
root@xu-ubuntu:~/scripts# echo $[RANDOM%50]
44
# 随机字体颜色
root@xu-ubuntu:~/scripts# echo -e "\033[1;$[RANDOM%7+31]mhello\033[0m"
hello
增强型赋值:
-= # i-=j 相当于 i=i-j
*=
/=
%=
++ # i++,++i 相当于 i=i+1
-- # i--,--i 相当于 i=i-1
root@xu-ubuntu:~/scripts# a=10
root@xu-ubuntu:~/scripts# let a+=20
root@xu-ubuntu:~/scripts# echo $a
30
root@xu-ubuntu:~/scripts# echo $[a+=10]
40
root@xu-ubuntu:~/scripts# echo $((a+=10))
50
i++与++i 。
# i++先赋值再+1
root@xu-ubuntu:~/scripts# i=1
root@xu-ubuntu:~/scripts# echo j=i++
j=i++
root@xu-ubuntu:~/scripts# let j=i++
root@xu-ubuntu:~/scripts# echo $i
2
root@xu-ubuntu:~/scripts# echo $j
1
# ++i先+1再赋值
root@xu-ubuntu:~/scripts# i=1
root@xu-ubuntu:~/scripts# let j=++i
root@xu-ubuntu:~/scripts# echo $j
2
root@xu-ubuntu:~/scripts# echo $i
2
true、false 。
bool值 | 二进制表示 | 含义 |
---|---|---|
true | 1 | 真 |
false | 0 | 假 |
与:&和相与结果为0,和1相与结果保留原值,一假则假,全真才真 。
或:|和1相或结果为1,和0相或结果保留原值,一真则真,全假才假 。
异或:
异或的两个值,相同为假,不同为真。两个数字X,Y异或得到结果Z,Z再和任意两者之一X异或,将得 。
出另一个值Y 。
root@xu-ubuntu:~/scripts# false
root@xu-ubuntu:~/scripts# echo $?
1
root@xu-ubuntu:~/scripts# true
root@xu-ubuntu:~/scripts# echo $?
0
# 变量互换
root@xu-ubuntu:~/scripts# x=10
root@xu-ubuntu:~/scripts# y=20
root@xu-ubuntu:~/scripts# let x=x^y
root@xu-ubuntu:~/scripts# let y=x^y
root@xu-ubuntu:~/scripts# let x=x^y
root@xu-ubuntu:~/scripts# echo $x $y
20 10
短路运算 。
短路&& 。
cmd1&&cmd2 。
当 cmd1 的结果为 false 时,整个表达式就是 false, cmd2 不参与运算,这就是所谓的短路 。
当 cmd1 的结果为 true 时,cmd2 才参与运算, 看cmd2的结果是真假,在整体判断 表达式的真假 。
短路|| 。
cmd1||cmd2 。
当 cmd1 的结果为 true 时,整个表达式就是 true, cmd2 不参与运算,这就是所谓的短路 。
当 cmd1 的结果为 false 时,cmd2 参与运算,根据cmd2的结果再去判断整个表达式的真假 。
[root@kylin-xu ~]# id tom > /dev/null 2>&1 && echo have || echo no
no
条件测试:
判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程, 。
实现评估布尔声明,以便用在条件性环境下进行执行 。
若真,则状态码变量 $? 返回0 。
若假,则状态码变量 $? 返回1 。
条件测试命令 。
test expr
[ arg... ] #同 test
[[ expression ]] #增加版的[],支持[],支持扩展正则表达式和通配符
# 注意:[] 中的表达式,前后要有空格
[[ expression ]] 用法
== #左侧字符串是否和右侧的PATTERN相同
#注意:此表达式用于[[ ]]中,PATTERN为通配符
=~ #左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
#注意: 此表达式用于[[ ]]中为扩展的正则表达式
常用选项:文件判断相关 。
注意:最终结果由用户对文件的实际权限决定,而非文件属性决定
-a FILE #如果文件存在则为真
-b FILE #如果文件为块特殊文件则为真
-c FILE #如果文件为字符特殊文件则为真
-d FILE #如果文件为目录则为真
-e FILE #如果文件存在则为真
-f FILE #如果文件存在且为常规文件则为真
-g FILE #如果文件的组属性设置打开则为真
-h FILE #如果文件为符号链接则为真
-L FILE #如果文件为符号链接则为真
-k FILE #如果文件的粘滞 (sticky) 位设定则为真
-p FILE #如果文件为命名管道则为真
-r FILE #如果当前用户对文件有可读权限为真
-s FILE #如果文件存在且不为空则为真
-S FILE #如果文件是套接字则为真
-t FD #如果文件描述符在一个终端上打开则为真
-u FILE #如果文件设置了SUID特殊权限则为真
-w FILE #如果当前用户对文件有可写权限为真
-x FILE #如果当前用户对文件有可执行权限为真
-O FILE #如果当前用户是文件属主为真
-G FILE #如果当前用户对文件属组为真
-N FILE #如果文件上次被读取之后修改过则为真
FILE1 -nt FILE2 #如果 file1 文件新于 file2 文件 则为真(根据修改日期)
FILE1 -ot FILE2 #如果 file1 文件旧于 file2 文件则为真
FILE1 -ef FILE2 #如果 file1 文件是 file2 文件的硬链接则为真
[root@kylin-xu ~]# [ -a /root/1.txt ] && echo exist || echo not
not
[root@kylin-xu ~]# touch 1.txt
[root@kylin-xu ~]# [ -a /root/1.txt ] && echo exist || echo not
exist
常用选项:字符串判断相关 。
在比较字符串时,建议放在""中 。
-z STRING #判断字符串是否为空,为空则为真
-n STRING #如果字符串不为空则为真
STRING #同上
[root@kylin-xu ~]# [[ -z "" ]]
[root@kylin-xu ~]# echo $?
0
[root@kylin-xu ~]# [[ -z "asdadasd" ]]
[root@kylin-xu ~]# echo $?
1
[root@kylin-xu ~]# [[ -n "" ]]
[root@kylin-xu ~]# echo $?
1
[root@kylin-xu ~]# [[ -n "asds" ]]
[root@kylin-xu ~]# echo $?
0
#两个字符串变量比较,一定要有空格,如果没有空格,就变成赋值了
STRING1 = STRING2 #如果 string1 和 string2 字符串相同则为真
STRING1 != STRING2 #如果 string1 和 string2 字符串不相同则为真
STRING1 < STRING2 #只比较第一个字符,以字母表顺序来比较,string1在string2 之前则为真,< 要转义
STRING1 > STRING2 #只比较第一个字符,以字母表顺序来比较,string1在string2 之后则为真, > 要转义
[root@kylin-xu ~]# [ "s" == "s" ] && echo true
true
[root@kylin-xu ~]# [ "s" == "s" ] && echo true || echo false
true
[root@kylin-xu ~]# [ "s" == "ssss" ] && echo true || echo false
false
[root@kylin-xu ~]# [ "s" \< "i" ] && echo true || echo false
false
[root@kylin-xu ~]# [ "s" \> "i" ] && echo true || echo false
true
[root@kylin-xu ~]# [ "s" != "i" ] && echo true || echo false
true
[root@kylin-xu ~]# [ "s" != "s" ] && echo true || echo false
false
# 使用正则进行匹配
[root@kylin-xu ~]# [[ $str =~ [a-z]+ ]];echo $?
0
[root@kylin-xu ~]# [[ $str =~ [0-9]+ ]];echo $?
1
常用选项:数学相关 。
#数字相关 格式 arg1 OP arg2
-eq #等于
-ne #不等于
-lt #小于
-le #小于等于
-gt #大于
-ge #大于等于
#算术表达式比较
== #相等
!= #不相等
<= #小于或等于
>= #大于或等于
< #小于
> #大于
[root@kylin-xu ~]# [ 10 -gt 9 ] && echo true || echo false
true
[root@kylin-xu ~]# [ 10 -lt 9 ] && echo true || echo false
false
[root@kylin-xu ~]# [ 10 -lt 99 ] && echo true || echo false
true
[root@kylin-xu ~]# [ 10 -eq 10 ] && echo true || echo false
true
[root@kylin-xu ~]# [ 10 -ne 100 ] && echo true || echo false
true
[root@kylin-xu ~]# [ 10 -ne 10 ] && echo true || echo false
false
常用选项:其它 。
-o OPTION #如果在shell 中开启了某项,则为真
-v VAR #如果变量被设置为为真
-R VAR #如果变量被设置且被引用则为真
! EXPR #表达式结果取反
EXPR1 -a EXPR2 #如果 expr1 和 expr2 都为真则为真 &&
EXPR1 -o EXPR2 #如果 expr1 和 expr2 有一个为真则为真
[root@kylin-xu ~]# x=100
[root@kylin-xu ~]# test -v x
[root@kylin-xu ~]# [ -v x ] && echo exist || echo "not exists"
exist
( cmd1;cmd2;... ) 和 { cmd1;cmd2;...; } 都可以将多个命令组合在一起,批量执行
( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境
{ list; } 不会开启子shell, 在当前shell中运行,会影响当前shell环境,左侧要有空格,右侧要有; 结束
[root@kylin-xu ~]# echo $BASHPID
553624
# 小括号开启子进程
[root@kylin-xu ~]# (echo $BASHPID ; str="aaa";echo $str);echo $str
592898
aaa
abc
# 大括号不会开子进程
[root@kylin-xu ~]# { echo $BASHPID ; str="aaa";echo $str; };echo $str
553624
aaa
aaa
# 1. 编写脚本 argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给 一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
#! /bin/bash
if [ "$#" -lt "1" ];then
echo "至少应该输入一个变量"
exit
fi
sed '/^$/p' -n "$1"|wc -l
#编写脚本 hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提 示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
if [[ $1 =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-4])$ ]];then
ping $1 -c1 -w1 > /dev/null 2>&1 && echo "该ip地址可以访问" || echo "该IP地址不能访问"
fi
#编写脚本 checkdisk.sh,检查磁盘分区空间和inode使用率,如果超过80%,就发广播警告空间将满
for i in `df -ih | sed '2,$p' -n | awk '{print $(NF-1),$1}' | column -t | sort -rn | awk -F "[% ]+" '$1>=80{print $2}'`;
do
echo $i"inode将满";
done
for i in `df -h | sed '2,$p' -n | awk '{print $(NF-1),$1}' | column -t | sort -rn | awk -F "[% ]+" '$1>=80{print $2}'`;
do
echo $i"空间将满";
done
# 编写脚本 per.sh,判断当前用户对指定参数文件,是否不可读并且不可写
if [ ! -r $1 -a ! -x $1 ];then
echo "不可读,不可写"
else
echo "可读或可写"
fi
# 编写脚本 excute.sh ,判断参数文件是否为sh后缀的普通文件,如果是,添加所有人可执行权限,否则提示用户非脚本文件
if [ -f $1 ];then
chmod +x $1
fi
# 编写脚本 nologin.sh和 login.sh,实现禁止和允许普通用户登录系统
nologin.sh
for i in `awk -F: '$3>=500&&$NF=="/bin/bash"{print $1}' /root/passwd`;
do
usermod -s /sbin/nologin $i
done
login.sh
for i in `awk -F: '$3>=500&&$NF=="/sbin/nologin"{print $1}' /etc/passwd`;
do
usermod -s /bin/bash $i;
done
使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置变量REPLY 。
[root@kylin-xu ~]# read
aaa
[root@kylin-xu ~]# echo $REPLY
aaa
[root@kylin-xu ~]# read -p "input:" NAME
input:xxx
[root@kylin-xu ~]# echo $NAME
xxx
管道符左右两侧都是子进程 。
[root@kylin-xu ~]# echo $BASHPID ; { echo $BASHPID; } | { xargs ;echo $BASHPID; }
654136
663419
663420
bash shell的配置文件很多,可以分成下面类别 。
全局配置:针对所有用户皆有效 。
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc ------------------------ /etc/bash.bashrc #ubuntu
个人配置:只针对特定用户有效 。
~/.bash_profile
~/.bashrc
配置文件生效和执行顺序:
#放在每个文件最前
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
~/ .bash_ profile
~/ .bashrc
/etc/bashrc #此文件执行两次
#放在每个文件最后
/etc/profile.d/*.sh
/etc/bashrc
/etc/profile
/etc/bashrc #此文件执行两次
~/.bashrc
~/.bash_profile
注意:文件之间的调用关系,写在同一个文件的不同位置,将影响文件的执行顺序 。
执行顺序:
/etc/profile.d/*.sh
/etc/bashrc
~/.bashrc
修改profile和bashrc文件后需生效两种方法
注意:source 会在当前shell中执行脚本,所有一般只用于执行置文件,或在脚本中调用另一个脚本的场景 。
保存在~/.bash_logout文件中(用户),在退出登录shell时运行 。
功能:
if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
# if单分支
if [ "$1" = "abc" ];then
echo "abc"
fi
# if多分枝
str=abc
if [ "$1" = "abc" ];then
echo "abc"
elif [ "$1" = "def" ];then
echo "def"
else
echo "haha"
fi
# if 嵌套
str=abc
if [ "$1" = "abc" ];then
echo "abc"
if [ "$2" = "def" ];then
echo "2 is def"
else
echo "2 not def"
fi
elif [ "$1" = "def" ];then
echo "def"
else
echo "haha"
fi
# 判断操作系统类型
. /etc/os-release
if [ $ID = "ubuntu" ];then
echo "is ubuntu"
elif [[ $ID =~ rhel|centos|rocky ]];then
echo "rhel"
else
echo "other system"
fi
case $1 in
"abc")
echo "is abc"
;;
"def")
echo "is def"
;;
"xyz")
echo "is xyz"
;;
"123")
echo "is 123"
;;
*)
echo "any"
;;
esac
read -p "" INPUT
INPUT=`echo $INPUT | tr "A-Z" "a-z"`
case $INPUT in
y|yes)
echo "is yes"
;;
n|no)
echo "is no"
;;
*)
echo "none"
;;
esac
# 文件后缀处理
read -p "请输入文件名:" FILE
FILE=`echo $FILE |sed 's#.*\.(.*$)#\1#g' -r`
case $FILE in
txt)
echo "is txt file"
;;
sh)
echo "is shell file"
;;
conf)
echo "is configure file"
;;
*)
echo "any"
;;
esac
将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件 。
重复运行次数 。
常见的循环的命令:for, while, until 。
执行机制:
for 循环列表生成方式:
# 计算1+2+3+...+100 的结果
sum=0
for i in {1..100}
do
let sum=$i+$sum
done
echo $sum
# 批量创建用户
[ $# -eq 0 ] && { echo "你没有输入变量";exit 1; }
for user in $@;
do
id $user &> /dev/null && echo "$user已经存在" || { useradd $user;echo "$user is create";id $user; }
done
# 99乘法表
flag=$[RANDOM%7+31]
for i in {1..9}
do
for j in {1..9}
do
if [ $j -le $i ];then
let k=$i*$j
echo -ne "\E[1;${flag}m$j×$i=$k\E[0m\t"
else
echo
break
fi
done
done
echo
有若干只兔和鸡,兔和鸡加起来一共有100条腿,请写一个简单的shell算出兔和鸡各多少只可能组合(假设所有的鸡和兔子的腿都是健全的,且兔和鸡至少为1只) 。
# 暴力破解
count=0
sum=0
num=0
for i in {1..100}
do
for j in {1..100}
do
let num++
let sum=4*$i+2*$j
if [ $sum == 100 ];then
echo "$i只兔子,$j只鸡"
let count++
fi
done
done
echo "共有$count种组合"
echo "循环了$num次"
# 第一次优化 加break
count=0
sum=0
num=0
for i in {1..100}
do
for j in {1..100}
do
let num++
let sum=4*$i+2*$j
if [ $sum == 100 ];then
echo "$i只兔子,$j只鸡"
let count++
break
fi
done
done
echo "共有$count种组合"
echo "循环了$num次"
# 第二次优化
count=0
sum=0
num=0
for i in {1..25}
do
for j in {1..50}
do
let num++
let sum=4*$i+2*$j
if [ $sum == 100 ];then
echo "$i只兔子,$j只鸡"
let count++
break
fi
done
done
echo "共有$count种组合"
echo "循环了$num次"
# 生成三角形
read -p "请输入三角形行数:" line
let num=$line*2-1
for i in `seq $line`
do
let star=$i*2-1
let space=($num-$star)/2
if [ $space == 0 ];then
for j in `seq $num`
do
echo -n "*"
done
else
for k in `seq $space`
do
echo -n " "
done
let x=$num-$space*2
for m in `seq $x`
do
echo -n "*"
done
fi
echo
done
请输入三角形行数:10
*
***
*****
*******
*********
***********
*************
***************
*****************
*******************
# 进度条
root@xu-ubuntu:~/scripts# for ((i = 0; i <= 100; ++i)); do printf "\e[4D%3d%%" $i;sleep 0.1s; done;echo
100%
while CONDITION; do COMMANDS; done
while CONDITION; do
循环体
done
while CONDITION
do
循环体
done
说明:
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为 。
“true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控 。
制变量;而此变量的值会在循环体不断地被修正 。
进入条件:CONDITION为 true 。
退出条件:CONDITION为 false 。
# 死循环
while true; do
循环体
done
while : ; do
循环体
done
# 计算1-100
root@xu-ubuntu:~/scripts# i=1
root@xu-ubuntu:~/scripts# while [ $i -le 100 ]; do let sum=$sum+$i;let i++; done
root@xu-ubuntu:~/scripts# echo $sum
5050
国际象棋棋盘 。
color=41
for i in {1..8}
do
for j in {1..8}
do
echo -ne "\E[0${color}m \E[0m"
if [ $color -eq 41 ];then
color=43
else
color=41
fi
done
if [ $color -eq 41 ];then
color=43
else
color=41
fi
echo
done
while特殊用法,while read 。
while 循环的特殊用法,遍历文件或文本的每一行 。
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
while read line
do
echo $line"---------"
done < /etc/passwd
continue:结束本次循环,进入下一次循环 。
break:结束当前循环,跳出当前的循环体 。
函数 function 。
是由若干条shell命令组成的语句块,实现代码重用和模块化编程 。
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分 。
函数和shell程序区别 。
#语法一
func_name(){
...函数体...
}
#语法二
function func_name {
...函数体...
}
#语法三
function func_name(){
...函数体...
}
func1(){
echo "func1"
}
function func2(){
echo "func2"
}
function func3 {
echo "func3"
}
func1
func2
func3
函数的调用方式 。
调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数代码 。
函数的生命周期:被调用时创建,返回时终止 。
# 交互式环境下
root@xu-ubuntu:~/scripts# test(){
> echo "hanshu"
> }
root@xu-ubuntu:~/scripts# test
hanshu
root@xu-ubuntu:~/scripts# function test1(){
> echo hanshu1
> }
root@xu-ubuntu:~/scripts# test1
hanshu1
root@xu-ubuntu:~/scripts# function test2 {
> echo hanshu3
> }
root@xu-ubuntu:~/scripts# test2
hanshu3
# 脚本文件中
function test1(){
echo hanshu1
}
function test2 {
echo hanshu2
}
test3(){
echo hanshu3
}
test1
test2
test3
root@xu-ubuntu:~/scripts# bash fun.sh
hanshu1
hanshu2
hanshu3
# 单独文件
fun.sh文件
function test1(){
echo hanshu1
}
function test2 {
echo hanshu2
}
test3(){
echo hanshu3
}
fun1.sh文件
. fun.sh
test1
test2
test3
root@xu-ubuntu:~/scripts# bash fun1.sh
hanshu1
hanshu2
hanshu3
# 在使用函数嵌套时一定要保证能够有一个出口,能有一个结束的地方,否则程序会不断地堆栈,报错
function test1(){
echo hanshu1
}
function test2 {
echo hanshu2
}
test3(){
echo hanshu3
test2
}
test3
root@xu-ubuntu:~/scripts# bash fun.sh
hanshu3
hanshu2
return 。
函数默认返回值是函数体中最后一条命令的退出状态码; 。
也可以使用return 自定义函数的返回值,在函数中使用 return,return 之后的语句将不会被执行 。
return 语句 | 返回值 |
---|---|
return | 由return前面一行的命令执行结果决定 |
return 0 | 无错误返回 |
return 1~255 | 有错误返回 |
function re1(){
echo aaa
return
echo bbb
}
function re2 {
echooo ccc
return
}
function re3 {
echo ddd
return 123
}
re1
echo "re1函数的返回值:$?"
echo "==============================="
re2
echo "re2函数的返回值:$?"
echo "==============================="
re3
echo "re3函数的返回值:$?"
echo "==============================="
root@xu-ubuntu:~/scripts# bash return.sh
aaa
re1函数的返回值:0
===============================
return.sh: line 21: echooo: command not found
re2函数的返回值:127
===============================
ddd
re3函数的返回值:123
===============================
return和exit的区别 。
return 只会中断函数执行,但exit 会中断整个脚本的执行 。
函数可以接受参数:
function canshu(){
echo -e "\E[1;$2m$1\E[0m"
return
}
yanse=$[RANDOM%7+31]
canshu "nihao" $yanse
echo "canshu的返回值是:$?"
function canshu(){
for i in $*
do
echo $i
done
}
canshu $*
root@xu-ubuntu:~/scripts# bash hanshucanshu.sh 1 2 3 4 5 6
1
2
3
4
5
6
变量作用域 。
变量类型 | 特点 |
---|---|
普通变量 | 只在当前shell进程中有效,函数外定义,可以在函数内修改 |
环境变量 | 当前shell和子shell有效 |
本地变量 | 作用域在函数内,函数结束会被自动销毁,只能在函数内定义 |
在函数中定义本地变量 。
local NAME=VALUE
# 普通变量可以在函数中被修改
root@xu-ubuntu:~/scripts# cat func1.sh
#!/bin/bash
function test1(){
echo "============test1 start============"
echo $var
var=789
echo $var
echo "============test1 end============"
}
root@xu-ubuntu:~/scripts# cat var.sh
#!/bin/bash
. func1.sh
var=123
function test2(){
echo "==========test2 start============"
echo $var
var=456
echo $var
echo "==========test2 end============"
}
function test3(){
echo "==========test3 start============"
echo $var
var=147
echo $var
echo "==========test3 end============"
}
echo $var
test1
echo $var
test2
echo $var
test3
echo $var
root@xu-ubuntu:~/scripts# bash var.sh
123
============test1 start============
123
789
============test1 end============
789
==========test2 start============
789
456
==========test2 end============
456
==========test3 start============
456
147
==========test3 end============
147
# 本地变量只作用在函数内
var=123
function test2(){
echo "==========test2 start============"
echo $var
local var=456
echo $var
echo "==========test2 end============"
}
function test3(){
echo "==========test3 start============"
echo $var
local var=147
echo $var
echo "==========test3 end============"
}
echo $var
test2
echo $var
test3
echo $var
# 函数中定义的var 只能在函数中被调用,作用域只在函数内
root@xu-ubuntu:~/scripts# bash var.sh
123
==========test2 start============
123
456
==========test2 end============
123
==========test3 start============
123
147
==========test3 end============
123
函数递归:
函数直接或间接调用自身,注意递归层数,可能会陷入死循环 。
递归特点
# 递归实现阶乘
function jiecheng(){
if [ $1 -eq 1 ];then
echo 1
else
echo $[$1 * $(jiecheng $[$1-1])]
fi
}
jiecheng $1
# 斐波那契数列第N项的值
fib(){
if [ $1 -gt 1 ];then
echo $[ $(fib $[$1-1]) + $(fib $[$1-2]) ]
else
echo $1
fi
}
fib $1
fork炸弹 。
fork 炸弹是一种恶意程序,它的内部是一个不断在 fork 进程的无限循环,实质是一个简单的递归程序.
由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源 。
:(){ :|:& };:
bomb(){ bomb | bomb & };bomb
trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能 。
在脚本或程序的执行过程中,我们可以通过发送信号的方式,打断或终止程序的执行过程,为了避免这种情况,我们可以使用信号捕捉,来自定义信号处理.
#常用选项
-l #显示所有信号
-p #显示所有自定义的信号
trap 'command' signal #自定义指定信号的处理方式
trap '' signal #忽略指定的信号
trap '-' signal #恢复信号默认操作
trap func EXIT #退出时执行func
显示所有的信号 。
root@xu-ubuntu:~/scripts# trap -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
。。。
信号的3种表示方法 。
3) SIGQUIT
3 #信号ID
SIGQUIT #完整写法,大小写都支持
QUIT #简短写法,大小写都支持
trap "echo '捕捉到了 ctrl+c信号'" SIGINT
trap "echo '捕捉到了 ctrl+\信号'" quit
trap -p
for i in {1..15};do
sleep 1
echo $i
done
trap '' int
trap -p
for i in {16..30};do
sleep 1
echo $i
done
trap '-' int
trap -p
for i in {31..45};do
sleep 1
echo $i
done
mktemp 命令用于创建并显示临时文件,可避免冲突 。
#常用选项
-d|--directory #创建目录
-u|--dry-run #只输出命令,不执行
-p DIR|--tmpdir[=DIR] #指明临时文件所存放目录位置
-t #将文件保存到$TMPDIR 定义的目录中,如果该变量未定义,则保存到/tmp 目录中
root@xu-ubuntu:~/scripts# mktemp
/tmp/tmp.mt31TMmcSK
# 如果指定了文件名,则后面一定要有X,至少要3个,X会被替换成随机串
root@xu-ubuntu:~/scripts# mktemp XXXX
vrWA
root@xu-ubuntu:~/scripts# ll vrWA
-rw------- 1 root root 0 Dec 2 10:30 vrWA
root@xu-ubuntu:~/scripts# mktemp testXXXX
testFo7g
root@xu-ubuntu:~/scripts# ll testFo7g
-rw------- 1 root root 0 Dec 2 10:30 testFo7g
# 赋值给变量
root@xu-ubuntu:~/scripts# file=`mktemp`
root@xu-ubuntu:~/scripts# echo $?
0
root@xu-ubuntu:~/scripts# echo $file
/tmp/tmp.Bz19OE4SHV
root@xu-ubuntu:~/scripts# echo 123 > $file
root@xu-ubuntu:~/scripts# cat $file
123
root@xu-ubuntu:~/scripts# ll $file
-rw------- 1 root root 4 Dec 2 10:31 /tmp/tmp.Bz19OE4SHV
# 创建目录
root@xu-ubuntu:~/scripts# mktemp -d
/tmp/tmp.N1GOey7oZo
root@xu-ubuntu:~/scripts# ll /tmp/tmp.N1GOey7oZo -d
drwx------ 2 root root 4096 Dec 2 10:33 /tmp/tmp.N1GOey7oZo/
install 功能相当于cp,chmod,chown,chgrp ,mkdir 等相关工具的集合 。
#常用选项
-m|--mode=MODE #指定权限,默认755
-v|--verbose #显示过程
-o|--owner=OWNER #指定属主
-g|--group=GROUP #指定属组
-d|--directory DIR #指定目录,如果不存在就创建
#如果目录不存在,则创建目录,并将权限设成777,如果目录存在,则修改其权限
root@xu-ubuntu:~/scripts# install -m 777 -d testdir
root@xu-ubuntu:~/scripts# ll testdir/ -d
drwxrwxrwx 2 root root 4096 Dec 2 10:35 testdir/
root@xu-ubuntu:~/scripts# install -m 755 -d testdir
root@xu-ubuntu:~/scripts# ll testdir/ -d
drwxr-xr-x 2 root root 4096 Dec 2 10:35 testdir/
#将文件复制到指定目录,并指定属主属组,权限,可以一步完成
root@xu-ubuntu:~/scripts# install -m 777 -o xu -g xu func1.sh /tmp/
root@xu-ubuntu:~/scripts# ll /tmp/func1.sh
-rwxrwxrwx 1 xu xu 483 Dec 2 10:38 /tmp/func1.sh*
变量:存储单个元素的内存空间 。
数组:存储多个元素的连续的内存空间,相当于多个变量的集合 。
数组名和索引 。
#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
#关联数组必须先声明,再使用
declare -A ARRAY_NAME
两者不能相互转换 。
# 一次只赋值一个元素
root@xu-ubuntu:~/scripts# num[0]=0
root@xu-ubuntu:~/scripts# num[1]=1
root@xu-ubuntu:~/scripts# echo ${num[0]}
0
# 一次性给所有元素赋值
root@xu-ubuntu:~/scripts# title=("ceo" "coo" "cto")
root@xu-ubuntu:~/scripts# echo ${title[1]}
coo
root@xu-ubuntu:~/scripts# echo ${title[2]}
cto
# 间断赋值
root@xu-ubuntu:~/scripts# week=([0]="sun" [4]="thur")
root@xu-ubuntu:~/scripts# echo ${week[0]}
sun
root@xu-ubuntu:~/scripts# echo ${week[1]}
root@xu-ubuntu:~/scripts# echo ${week[4]}
thur
# 使用read接收赋值
root@xu-ubuntu:~/scripts# read -a test
a b c d
root@xu-ubuntu:~/scripts# echo ${test[1]}
b
root@xu-ubuntu:~/scripts# declare -a
declare -a BASH_ARGC=([0]="0")
declare -a BASH_ARGV=()
declare -a BASH_COMPLETION_VERSINFO=([0]="2" [1]="11")
declare -a BASH_LINENO=()
declare -a BASH_REMATCH=()
declare -a BASH_SOURCE=()
declare -ar BASH_VERSINFO=([0]="5" [1]="1" [2]="16" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")
declare -a DIRSTACK=()
declare -a FUNCNAME
declare -a GROUPS=()
declare -a PIPESTATUS=([0]="0")
declare -a num=([0]="0" [1]="1")
declare -a test=([0]="a" [1]="b" [2]="c" [3]="d")
declare -a title=([0]="ceo" [1]="coo" [2]="cto")
declare -a week=([0]="sun" [4]="thur")
# 引用特定的数组元素
#如果省略[INDEX]表示引用下标为0的元素
${ARRAY_NAME[INDEX]}
root@xu-ubuntu:~/scripts# arr=({01..10})
root@xu-ubuntu:~/scripts# echo ${arr[2]}
03
root@xu-ubuntu:~/scripts# echo ${arr[4]}
05
# 如果不加index,默认是输出第一个值
root@xu-ubuntu:~/scripts# echo ${arr}
01
# 引用数组所有的元素
root@xu-ubuntu:~/scripts# echo ${arr[*]}
01 02 03 04 05 06 07 08 09 10
root@xu-ubuntu:~/scripts# echo ${arr[@]}
01 02 03 04 05 06 07 08 09 10
# 遍历数组
root@xu-ubuntu:~/scripts# arr=({01..05})
root@xu-ubuntu:~/scripts# for i in ${arr[*]};do echo $i ; done
01
02
03
04
05
root@xu-ubuntu:~/scripts# for i in ${arr[@]};do echo $i ; done
01
02
03
04
05
# 取出下标
root@xu-ubuntu:~/scripts# echo ${!arr[*]}
0 1 2 3 4
root@xu-ubuntu:~/scripts# unset arr[2] # 取消变量
root@xu-ubuntu:~/scripts# echo ${!arr[*]}
0 1 3 4
root@xu-ubuntu:~/scripts# for i in ${!arr[@]};do echo "$i--->${arr[$i]}" ; done
0--->01
1--->02
3--->04
4--->05
# 数组长度
root@xu-ubuntu:~/scripts# echo ${#arr[*]}
4
删除数组中的某元素,会导致稀疏格式 。
unset ARRAY[INDEX]
删除整个数组 。
unset ARRAY
root@xu-ubuntu:~/scripts# arr=({a..d})
root@xu-ubuntu:~/scripts# echo ${arr[*]}
a b c d
root@xu-ubuntu:~/scripts# echo ${arr[*]:1:2}
b c
# 从1位置开始取到末尾
root@xu-ubuntu:~/scripts# echo ${arr[*]:1}
b c
root@xu-ubuntu:~/scripts# arr[4]=1234
root@xu-ubuntu:~/scripts# echo ${arr[4]}
1234
root@xu-ubuntu:~/scripts# arr[${#arr[*]}]=haha
root@xu-ubuntu:~/scripts# arr[${#arr[*]}]=haha
root@xu-ubuntu:~/scripts# arr[${#arr[*]}]=haha
root@xu-ubuntu:~/scripts# arr[${#arr[*]}]=haha
root@xu-ubuntu:~/scripts# echo ${arr[*]}
a b c d 1234 haha haha haha haha
关联数组与普通数组区别:
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
root@xu-ubuntu:~/scripts# declare -A arr1
root@xu-ubuntu:~/scripts# arr1[name]="xu"
root@xu-ubuntu:~/scripts# arr1[age]=18
root@xu-ubuntu:~/scripts# arr1[gender]=男
root@xu-ubuntu:~/scripts# echo $arr1
root@xu-ubuntu:~/scripts# echo ${arr1[name]}
xu
root@xu-ubuntu:~/scripts# echo ${arr1[gender]}
男
root@xu-ubuntu:~/scripts# declare -A arr2
# 一次性定义完成
root@xu-ubuntu:~/scripts# arr2=([name]="aaa" [age]=18 [gender]="男")
root@xu-ubuntu:~/scripts# echo ${arr2[name]}
aaa
# 取出index
root@xu-ubuntu:~/scripts# echo ${!arr1[*]}
gender age name
root@xu-ubuntu:~/scripts# echo ${!arr2[*]}
gender age name
# 单独取出index是,顺序是不一定的,也就是说如果我们根据index遍历关联数组的结果顺序不是按照我们定义时的去输出
root@xu-ubuntu:~/scripts# for i in ${!arr2[*]} ;do echo "$i---${arr2[$i]}" ;done
gender---男
age---18
name---aaa
# 生成10个随机数保存于数组中,并找出其最大值和最小值
function SORT(){
temp=0
for j in ${!arr[*]}
do
let k=$j+1
for k in ${!arr[*]}
do
if [ ${arr[$j]} -gt ${arr[$k]} ];then
temp=${arr[$j]}
arr[$j]=${arr[$k]}
arr[$k]=$temp
fi
done
done
echo "max in arr is ${arr[1]}"
echo "min in arr is ${arr[$j]}"
}
for i in `seq 10`
do
arr[$i]=$RANDOM
done
echo "arr is ${arr[*]}"
SORT arr
# #返回字符串变量var的字符的长度,一个汉字算一个字符
root@xu-ubuntu:~/scripts# echo ${#str}
6
root@xu-ubuntu:~/scripts# str=123456
#返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)
root@xu-ubuntu:~/scripts# echo ${str:2}
3456
root@xu-ubuntu:~/scripts# echo ${str:2:2}
34
#取字符串的最右侧几个字符, 注意:冒号后必须有一空白字符
root@xu-ubuntu:~/scripts# echo ${str: -2}
56
root@xu-ubuntu:~/scripts# echo ${str: 2: -2}
34
#其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以第一个word为界删左留右
${var#*word}
#从var变量的值中删除以word开头的部分
${var#word}
#同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容,即贪婪模式,以最后一个word为界删左留右
${var##*word}
${var##word}
root@xu-ubuntu:~/scripts# echo $str
abcd1234abcd1234
root@xu-ubuntu:~/scripts# echo ${str#abc}
d1234abcd1234
root@xu-ubuntu:~/scripts# echo ${str#*abc}
d1234abcd1234
root@xu-ubuntu:~/scripts# echo ${str#*cd}
1234abcd1234
# 贪婪匹配
root@xu-ubuntu:~/scripts# echo ${str##*cd}
1234
root@xu-ubuntu:~/scripts# echo ${str##*abcd}
1234
#其中word可以是指定的任意字符,功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以从右向左的第一个word为界删右留左
${var%word*}
${var%word}
#同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符,即贪婪模式,以从右向左的最后一个word为界删右留左
${var%%word*}
${var%%word}
[root@web01 ~]# str=abcd1234abcd12345
[root@web01 ~]# echo $str
abcd1234abcd12345
[root@web01 ~]# echo ${str%abc*}
abcd1234
[root@web01 ~]# echo ${str%345}
abcd1234abcd12
[root@web01 ~]# echo ${str%%34}
abcd1234abcd12345
[root@web01 ~]# echo ${str%%34*}
abcd12
#查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之,懒惰模式
${var/pattern/substr}
#查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之,贪婪模式
${var//pattern/substr}
#查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}
#查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}
[root@ubuntu2204 ~]# str=abcd1234abcd12345
#从左到右,替换第一个123为XYZ
[root@ubuntu2204 ~]# echo ${str/123/XYZ}
abcdXYZ4abcd12345
#从左到右,替换所有123为XYZ
[root@ubuntu2204 ~]# echo ${str//123/XYZ}
abcdXYZ4abcdXYZ45
#替换行首的abc为XYZ
[root@ubuntu2204 ~]# echo ${str/#abc/XYZ}
XYZd1234abcd12345
#替换行尾的12345为XYZ
[root@ubuntu2204 ~]# echo ${str/%12345/XYZ}
abcd1234abcdXYZ
# 删除就是将查找到的替换为空
#删除var表示的字符串中第一次被pattern匹配到的字符串,懒惰模式
${var/pattern}
#删除var表示的字符串中所有被pattern匹配到的字符串,贪婪模式
${var//pattern}
#删除var表示的字符串中所有以pattern为行首匹配到的字符串
${var/#pattern}
#删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串
${var/%pattern}
[root@ubuntu2204 ~]# str=abcd1234abcd12345
#从左到右删除第一个1234
[root@ubuntu2204 ~]# echo ${str/1234}
abcdabcd12345
#从左到右删除所有1234
[root@ubuntu2204 ~]# echo ${str//1234}
abcdabcd5
#删除行首abcd
[root@ubuntu2204 ~]# echo ${str/#abcd}
1234abcd12345
#删除行尾12345
[root@ubuntu2204 ~]# echo ${str/%12345}
abcd1234abcd
#把var中的所有小写字母转换为大写
${var^^}
#把var中的所有大写字母转换为小写
${var,,}
[root@web01 ~]# str=abcd1234ABCD12345
[root@web01 ~]# echo ${str^^}
ABCD1234ABCD12345
[root@web01 ~]# echo ${str,,}
abcd1234abcd12345
最后此篇关于Shell编程的文章就讲到这里了,如果你想了解更多关于Shell编程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试打印 timeval 类型的值。实际上我可以打印它,但我收到以下警告: 该行有多个标记 格式“%ld”需要“long int”类型,但参数 2 的类型为“struct timeval” 程序
我正在编写自己的 unix 终端,但在执行命令时遇到问题: 首先,我获取用户输入并将其存储到缓冲区中,然后我将单词分开并将它们存储到我的 argv[] 数组中。IE命令是“firefox”以启动存储在
我是 CUDA 的新手。我有一个关于一个简单程序的问题,希望有人能注意到我的错误。 __global__ void ADD(float* A, float* B, float* C) { con
我有一个关于 C 语言 CGI 编程的一般性问题。 我使用嵌入式 Web 服务器来处理 Web 界面。为此,我在服务器中存储了一个 HTML 文件。在此 HTML 文件中包含 JavaScript 和
**摘要:**在代码的世界中,是存在很多艺术般的写法,这可能也是部分程序员追求编程这项事业的内在动力。 本文分享自华为云社区《【云驻共创】用4种代码中的艺术试图唤回你对编程的兴趣》,作者: break
我有一个函数,它的任务是在父对象中创建一个变量。我想要的是让函数在调用它的级别创建变量。 createVariable testFunc() [1] "test" > testFunc2() [1]
以下代码用于将多个连续的空格替换为1个空格。虽然我设法做到了,但我对花括号的使用感到困惑。 这个实际上运行良好: #include #include int main() { int ch, la
我正在尝试将文件写入磁盘,然后自动重新编译。不幸的是,某事似乎不起作用,我收到一条我还不明白的错误消息(我是 C 初学者 :-)。如果我手动编译生成的 hello.c,一切正常吗?! #include
如何将指针值传递给结构数组; 例如,在 txt 上我有这个: John Doe;xxxx@hotmail.com;214425532; 我的代码: typedef struct Person{
我尝试编写一些代码来检索 objectID,结果是 2B-06-01-04-01-82-31-01-03-01-01 . 这个值不正确吗? // Send a SysObjectId SNMP req
您好,提前感谢您的帮助, (请注意评论部分以获得更多见解:即,以下示例中的成本列已添加到此问题中;西蒙提供了一个很好的答案,但成本列本身并未出现在他的数据响应中,尽管他提供的功能与成本列一起使用) 我
我想知道是否有人能够提出一些解决非线性优化问题的软件包的方法,而非线性优化问题可以为优化解决方案提供整数变量?问题是使具有相等约束的函数最小化,该函数受某些上下边界约束的约束。 我已经在R中使用了'n
我是 R 编程的初学者,正在尝试向具有 50 列的矩阵添加一个额外的列。这个新列将是该行中前 10 个值的平均值。 randomMatrix <- generateMatrix(1,5000,100,
我在《K&R II C 编程 ANSI C》一书中读到,“>>”和“0; nwords--) sum += *buf++; sum = (sum >>
当下拉列表的选择发生变化时,我想: 1) 通过 div 在整个网站上显示一些 GUI 阻止覆盖 2)然后处理一些代码 3) 然后隐藏叠加层。 问题是,当我在事件监听器函数中编写此逻辑时,将执行 onC
我正在使用 Clojure 和 RESTEasy 设计 JAX-RS REST 服务器. 据我了解,用 Lisp 系列语言编写的应用程序比用“传统”命令式语言编写的应用程序更多地构建为“特定于领域的语
我目前正在研究一种替代出勤监控系统作为一项举措。目前,我设计的用户表单如下所示: Time Stamp Userform 它的工作原理如下: 员工将选择他/她将使用的时间戳类型:开始时间、超时、第一次
我是一名学生,试图自学编程,从在线资源和像您这样的人那里获得帮助。我在网上找到了一个练习来创建一个小程序来执行此操作: 编写一个程序,读取数字 a 和 b(长整型)并列出 a 和 b 之间有多少个数字
我正在尝试编写一个 shell 程序,给定一个参数,打印程序的名称和参数中的每个奇数词(即,不是偶数词)。但是,我没有得到预期的结果。在跟踪我的程序时,我注意到,尽管奇数词(例如,第 5 个词,5 %
只是想知道是否有任何 Java API 可以让您控制台式机/笔记本电脑外壳上的 LED? 或者,如果不可能,是否有可能? 最佳答案 如果你说的是前面的 LED 指示电源状态和 HDD 繁忙状态,恐怕没
我是一名优秀的程序员,十分优秀!