常用命令
本文为bash shell前面29章补充内容,无先后顺序,记录一些遇到的书中未提及的内容
30.1 COMMANDS
1. set
set -e
脚本前面设置该选项后,当脚本中任何以一个命令执行返回的状态码不为0
时就退出整个脚本(默认脚本运行中某行报错会继续往下执行),这样就不必设置很多的判断条件去判断每个命令是否执行成功,特别那些依赖很强的地方,脚本任何一处执行报错就不应继续执行其他语句的时候就特别有用,之前写的一些像LAMP的安装脚本就深有体会。例如:
#!/bin/bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
set -u
设置该选项后,当脚本在执行过程中尝试使用未定义过的变量时,报错并退出运行整个脚本(默认会把该变量的值当作空来处理),这个也非常有用,有些时候可能在脚本中变量很多很长,疏忽了把某个变量的名字写错了,这时候运行脚本不会报任何错误,但结果却不是你想要的,排查起来很是头疼,使用这个选项就完美的解决了这个问题。
set -x
与 set +x
命令的作用实际是用于输出详细日志,是Shell脚本中使用echo命令输出的替代方案。更适用于输出大量日志的场景使用
set -x 是开启,set +x是关闭,set -o是查看 (xtrace,追踪一段代码的显示情况)
[root@node-249 learn-terraform-docker-container]# set -x
+ set -x
++ printf '\033]0;%s@%s:%s\007' root node-249 /opt/learn-terraform-docker-container
2. cut
基本语法
cut [选项参数] filename
说明:默认分隔符是制表符
选项与参数:
-d:分隔符,按照指定分隔符分割列。与 -f 一起使用
-f:依据 -d 的分隔字符将一段信息分割成为数段,用 -f 取出第几段的意思(列号,提取第几列)
-c:以字符 (characters) 的单位取出固定字符区间
-b:以字节为单位进行分割
示例:
-d+-f
切割列
[root@node-251 yurq]# cut -d ':' -f 1 /etc/passwd #切割第一列
root
bin
daemon
...
[root@node-251 yurq]# cut -d ':' -f 2,4 /etc/passwd #切割第二、四列
x:0
x:1
x:2
...
[root@node-251 yurq]# cut -d ':' -f 2-4 /etc/passwd #切割第二到四列
x:0:0
x:1:1
x:2:2
x:3:4
-c
切割按字符位字符
[root@node-251 yurq]# cut -c 1-4 /etc/passwd
root
bin:
daem
-b
按字节位切割
[root@node-251 yurq]# cut -b 1,5,9 /etc/passwd
r::
bx1
do:
3. chattr
基本语法
chattr [+-=] [属性] 文件或目录名
+
表示给文件或目录添加属性,
-
表示移除文件或目录拥有的某些属性
=
表示给文件或目录设定一些属性
chattr 命令常用的属性选项及功能
属 性 | 说明 |
---|---|
i | 如果对文件设置 i 属性,那么不允许对文件进行删除、改名,也不能添加和修改数据; 如果对目录设置 i 属性,那么只能修改目录下文件中的数据,但不允许建立和删除文件; |
a | 如果对文件设置 a 属性,那么只能在文件中増加数据,但是不能删除和修改数据;如果对目录设置 a 属性,那么只允许在目录中建立和修改文件,但是不允许删除文件; |
u | 设置此属性的文件或目录,在删除时,其内容会被保存,以保证后期能够恢复,常用来防止意外删除文件或目录。 |
s | 和 u 相反,删除文件或目录时,会被彻底删除(直接从硬盘上删除,然后用 0 填充所占用的区域),不可恢复。 |
i
属性可用于系统文件保护,a
属性可用于内容备份
示例:
i
属性演示
[root@node-251 yurq]# mkdir test_dir;touch test_file #建立文件及目录
[root@node-251 yurq]# ll
total 4
-rw-r--r-- 1 root root 0 May 31 23:33
-rwxr--r-- 1 root root 90 Jun 1 16:46 lock.sh
drwxr-xr-x 2 root root 24 Apr 24 22:56 sql_data_bak
drwxr-xr-x 2 root root 6 Jun 1 21:27 test_dir
-rw-r--r-- 1 root root 0 Jun 1 21:27 test_file
[root@node-251 yurq]# chattr +i test_file #添加属性
[root@node-251 yurq]# ll
total 4
-rw-r--r-- 1 root root 0 May 31 23:33
-rwxr--r-- 1 root root 90 Jun 1 16:46 lock.sh
drwxr-xr-x 2 root root 24 Apr 24 22:56 sql_data_bak
drwxr-xr-x 2 root root 6 Jun 1 21:27 test_dir
-rw-r--r-- 1 root root 0 Jun 1 21:27 test_file
[root@node-251 yurq]# rm -rf test_file #无法删除文件
rm: cannot remove ‘test_file’: Operation not permitted
[root@node-251 yurq]# mv test_file test_file_1 #无法重命名文件
mv: cannot move ‘test_file’ to ‘test_file_1’: Operation not permitted
[root@node-251 yurq]# echo 123 > test_file #无法修改内容
-bash: test_file: Permission denied
[root@node-251 yurq]# chattr +i test_dir/ #添加目录属性
[root@node-251 yurq]# rm -rf test_dir #无法删除目录
rm: cannot remove ‘test_dir’: Operation not permitted
[root@node-251 yurq]# echo 123> test_dir/test #无法修改目录内文件内容
-bash: test_dir/test: Permission denied
[root@node-251 yurq]# chattr -i test_dir/
[root@node-251 yurq]# echo 123> test_dir/test
[root@node-251 yurq]# chattr +i test_dir/
[root@node-251 yurq]# echo 321 >> test_dir/test
[root@node-251 yurq]# rm -rf test_dir/test #无法删除文件
rm: cannot remove ‘test_dir/test’: Permission denied
a
属性显示
[root@localhost ~]# mkdir -p /back/log
#建立备份目录
[root@localhost ~]# chattr +a /back/log
#赋予a属性
[root@localhost ~]# cp /var/log/messages /back/log
#可以复制文件和新建文件到指定目录中
[root@localhost ~]# rm -rf /back/log/messages
rm: cannot remove '/back/log/messages': Permission denied
#无法删除 /back/log/messages,操作不允许
4. getfacl/setfacl(ACL)
当一个文件要对多个用户设置不同的使用权限是,简单的权限管理(一个用户,一个组,一个其他人)已经无法满足需求.
这个时候,我们就需要使用ACL权限 (Access Control list访问控制列表)
,对文件的ACL设置可以通过ACL让特定的用户或组对其设置特别的权限
ACL和UGO的区别
UGO基本权限:只能一个用户、一个组合、其他人这三个对象进行授权
ACL文件权限管理:设置不同用户,不同的基本权限(r、w、x),对象数量不同
所谓的 ugo 就是指 user(也称为 owner)、group 和 other 三个单词的首字母组合
setfacl命令(set设置,f文件file,acl访问控制列表)添加文件的ACL
基本语法
setfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ...
参数:
-m:设定 ACL 权限。如果是给予用户 ACL 权限,则使用"u:用户名:权限"格式赋予;如果是给予组 ACL 权限,则使用"g:组名:权限" 格式赋予;
-x:删除指定的 ACL 权限;
-b:删除所有的 ACL 权限;
-d:设定默认 ACL 权限。只对目录生效,指目录中新建立的文件拥有此默认权限;
-k:删除默认 ACL 权限;
-R:递归设定 ACL 权限。指设定的 ACL 权限会对目录下的所有子文件生效;
1. 给用户添加ACL
[root@localhost ~]# useradd zhangsan
[root@localhost ~]# useradd lisi
[root@localhost ~]# useradd st
[root@localhost ~]# groupadd tgroup
#添加需要试验的用户和用户组,省略设定密码的过程
[root@localhost ~]# mkdir /project #建立需要分配权限的目录
[root@localhost ~]# chown root:tgroup /project/
#改变/project目录的属主和属组
[root@localhost ~]# chmod 770 /project/
#指定/project目录的权限
[root@localhost ~]# ll -d /project/
drwxrwx--- 2 root tgroup 4096 1月19 04:21 /project/
#查看一下权限,已经符合要求了
#这时st学员来试听了,如何给她分配权限
[root@localhost ~]# setfacl -m u:st:rx /project/
#给用户st赋予r-x权限,使用"u:用户名:权限" 格式
[root@localhost /]# cd /
[root@localhost /]# ll -d project/
drwxrwx---+ 3 root tgroup 4096 1月19 05:20 project/
#使用ls-l査询时会发现,在权限位后面多了一个"+",表示此目录拥有ACL权限
[root@localhost /]# getfacl project
#查看/prpject目录的ACL权限
#file: project <-文件名
#owner: root <-文件的属主
#group: tgroup <-文件的属组
user::rwx <-用户名栏是空的,说明是属主的权限
user:st:r-x <-用户st的权限
group::rwx <-组名栏是空的,说明是属组的权限
mask::rwx <-mask权限
other::--- <-其他人的权限
2. 给用户组赋予 ACL 权限
[root@localhost /]# groupadd tgroup2
#添加测试组
[root@localhost /]# setfacl -m g:tgroup2:rwx project/
#为组tgroup2纷配ACL权限,使用"g:组名:权限"格式
[root@localhost /]# ll -d project/
drwxrwx---+ 2 root tgroup 4096 1月19 04:21 project/
#属组并没有更改
[root@localhost /]# getfacl project/
#file: project/
#owner: root
#group: tgroup
user::rwx
user:st:r-x
group::rwx
group:tgroup2:rwx <-用户组tgroup2拥有了rwx权限
mask::rwx
other::--
3. 最大有效权限mask
mask 是用来指定最大有效权限的。mask 的默认权限是 rwx,如果我给 st 用户赋予了 r-x 的 ACL 权限,mj 需要和 mask 的 rwx 权限"相与"才能得到 st 的真正权限,也就是 r-x "相与"rwx出的值是 r-x,所以 st 用户拥有 r-x 权限。
如果把 mask 的权限改为 r–,和 st 用户的权限相与,也就是 r–"相与"r-x 得出的值是 r–,st 用户的权限就会变为只读。大家可以这么理解:用户和用户组所设定的权限必须在 mask 权限设定的范围之内才能生效,mask权限就是最大有效权限。
不过我们一般不更改 mask 权限,只要给予 mask 最大权限 rwx,那么任何权限和 mask 权限相与,得出的值都是权限本身。也就是说,我们通过给用户和用户组直接赋予权限,就可以生效,这样做更直观。
[root@localhost /]# setfacl -m m:rx project/
#设定mask权限为r-x,使用"m:权限"格式
[root@localhost /]# getfacl project/
#file:project/
#owner:root
#group:tgroup
user::rwx
group::rwx #effective:r-x
mask::r-x
#mask权限变为r-x
other::--
4. 递归 ACL 权限
默认 ACL 权限的作用是:如果给父目录设定了默认 ACL 权限,那么父目录中所有新建的子文件都会继承父目录的 ACL 权限
[root@localhost project]# setfacl -m u:st:rx -R /project/
#-R递归
[root@localhost project]# ll
总用量8
-rw-r-xr--+ 1 root root 01月19 05:20 abc
-rw-rwx--+ 1 root root 01月19 05:33 bcd
drwxr-xr-x+ 2 root root 4096 1月19 05:20 d1
drwxrwx--+ 2 root root 4096 1月19 05:33 d2
#abc和d1也拥有了ACL权限
5. 删除ACL权限
删除指定的ACL权限:
[root@localhost /]# setfacl -x u:st /project/
#删除指定用户和用户组的ACL权限
[root@localhost /]# getfacl project/
# file:project/
# owner: root
# group: tgroup
user::rwx
group::rwx
group:tgroup2:rwx
mask::rwx
other::--
#st用户的权限已被删除
删除所有ACL权限:
[root@localhost /]# setfacl -b project/
#会删除文件的所有ACL权限
[root@localhost /]# getfacl project/
#file: project/
#owner: root
# group: tgroup
user::rwx
group::rwx
other::--
#所有ACL权限已被删除
5. dd
dd 命令用于读取、转换并输出数据。可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。
语法格式:
dd [operand]
dd option
常用参数:
-
bs=BYTES:同时设置输入/输出的块大小为BYTES字节
-
count=N:仅拷贝N个block块,块大小等于ibs指定的字节数
-
cbs=BYTES:一次转换BYTES个字节,即指定转换缓冲区大小
-
ibs=BYTES:一次读取BYTES个字节,即指定读取的一个块大小为BYTES字节
-
obs=BYTES:一次输出BYTES个字节,即指定输出的一个块大小为BYTES字节
-
if=FILE:输入文件名,默认为标准输入
-
iflag=FLAGS
-
of=FILE:输出文件名,默认为标准输出
-
oflag=FLAGS
-
skip=N:从输入文件开头跳过N个block块后开始复制
-
seek=N:从输出文件开头跳过N个block块后开始复制
-
status=LEVEL:打印到stderr的信息级别
node: 抑制错误消息之外的所有内容 noxfer: 抑制最终的传输统计数据 progress:显示定期传输统计信息
-
conv=
<CONVS KEY WORDS>
,设置关键字信息来转换文件。默认值conv=sync
,参数conv的关键字有以下几种:conversion:用指定的参数转换文件。 ascii:转换ebcdic为ascii ebcdic:转换ascii为ebcdic ibm:转换ascii为alternate ebcdic block:把每一行转换为长度为cbs,不足部分用空格填充 unblock:使每一行的长度都为cbs,不足部分用空格填充 lcase:把大写字符转换为小写字符 ucase:把小写字符转换为大写字符 swap:交换输入的每对字节 noerror:出错时不停止 notrunc:不截短输出文件 sync:将每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。
生成一个200M的新文件。/dev/zero的文件可以产生无穷的数据,使用这个设备配合dd使用,就能创建一个指定大小的文件。
[root@localhost ~]# dd if=/dev/zero of=1.txt bs=10M count=20
记录了20+0 的读入
记录了20+0 的写出
209715200字节(210 MB)已复制,3.69047 秒,56.8 MB/秒
[root@localhost ~]# ll -h 1.txt
-rw-r--r--. 1 root root 200M 2月 26 11:16 1.txt
6. /dev/zero
该文件是一个特殊的字符设备文件,当我们使用或者读取它的时候,它会提供无限连续不断的空数据流(特殊的数据格式流),其中一个典型的用法就是用它提供的字符流来覆盖信息,另一个常见用法就是产生一个特定大小的空白文件。
示例:
生成块大小1M,含有2个块的文件,在使用dd 命令产生空文件时常用/dev/zero作为字符流的源
dd if=/dev/zero of=1.txt bs=1M count=2
利用/dev/zero文件覆盖其他文件信息,创建一个新文件写入12345字符串,用空的字符流覆盖存在的2.txt文件
echo 12345>2.txt
cat 2.txt
12345
dd if=/dev/zero of=2.txt bs=1M count=6
cat 2.txt
7. getopts
getopts命令是用来解析Shell脚本命令行参数
的工具,getopts命令参数包含需要被识别的选项字符,如果选项字符后面跟着一个冒号,则表明该字符选项需要一个命令行参数(选项字符与对应的命令行参数之间以空格分隔)(注意:冒号&问号不能被用作为选项字符)。getopts命令每次被调用时,它会将下一个选项字符放置到变量中,OPTARG则可以拿到参数值;如果option前面加冒号,则代表忽略错误
命令使用格式
getopts optstring name [arg...]
- optstring列出了对应的Shell脚本可以识别的所有参数,例如:需要使用-a,-f,-s参数时,optstring是afs;如果需要命令行参数后面还跟随一个值,则在相应的optstring后面加冒号,例如a:fs 表示a命令行参数后面会有一个值,是**-a value**的形式;
- getopts执行时若匹配到a参数,会把-a参数对应的value存放在一个叫OPTARG的Shell Variable中;
- 如果optstring是以冒号开头,则表明当命令行出现了optstring中没有的参数将不会提示错误信息
#!/bin/bash
func() {
echo "Usage:"
echo "test.sh [-j S_DIR] [-m D_DIR]"
echo "Description:"
echo "S_DIR,the path of source."
echo "D_DIR,the path of destination."
exit -1
}
upload="false"
while getopts 'h:j:m:u' OPT; do
case $OPT in
j) S_DIR="$OPTARG";;
m) D_DIR="$OPTARG";;
u) upload="true";;
h) func;;
?) func;;
esac
done
echo $S_DIR
echo $D_DIR
echo $upload
sh test.sh -j /data/usw/web -m /opt/data/web
##输出结果
/data/usw/web
/opt/data/web
false
getopts命令的选项字符中,如果是没有跟随 : 的字母就是开关型选项,不需要指定值,等同于true/false,只要带上了这个参数就是true。如下示例中的 -u
getopts命令识别出各个选项之后,就可以配合case进行操作。操作中,有两个"常量",一个是OPTARG
,用来获取当前选项的值;另外一个就是OPTIND
,表示当前选项在参数列表中的位移。case的最后一项是?,是用来识别非法的选项,并进行相应的操作,我们的脚本中是输出了帮助信息
sh test.sh -j /data/usw/web -m /opt/data/web -u
##输出结果
/data/usw/web
/opt/data/web
true
8. 彩色echo
echo -e "\033[31m123\033[0m" #red
echo -e "\033[32m123\033[0m" #green
echo -e "\033[33m123\033[0m" #yellow
echo -e "\033[34m123\033[0m" #blue
9. bc -l
[root@server shells]# threshold=1
[root@server shells]# mem_info=$(free | awk 'NR==2{print $3/$2 * 100}')
[root@server shells]# echo "$mem_info > $threshold"
51.3596 > 1
[root@server shells]# echo "$mem_info > $threshold"|bc -l
1
把字符串提供给bc进行计算
10. /bin/sh -c
比如要向 123 文件中随便写入点内容,可以:
[yurq@node-137 ~]$ echo 123 > /root/123
-bash: /root/123: Permission denied
然后,我们使用 sudo 并配合 echo 命令再次向修改权限之后的 123 文件中写入信息:
[yurq@node-137 ~]$ sudo echo 123 > /root/123
-bash: /root/123: Permission denied
这时可以看到 bash 拒绝这么做,说是权限不够。这是因为重定向符号 “>” 和 “>>” 也是 bash 的命令。我们使用 sudo 只是让 echo 命令具有了 root 权限,但是没有让 “>” 和 “>>” 命令也具有 root 权限,所以 bash 会认为这两个命令都没有像 123 文件写入信息的权限。
解决这一问题的途径有两种。
- 第一种是利用 “
sh -c
” 命令,它可以让 bash 将一个字串作为完整的命令来执行,这样就可以将 sudo 的影响范围扩展到整条命令。具体用法如下:
[yurq@node-137 ~]$ sudo /bin/sh -c 'echo "123" > /root/123'
- 另一种方法是利用管道和
tee
命令,该命令可以从标准输入中读入信息并将其写入标准输出或文件中,具体用法如下:
[yurq@node-137 ~]$ echo "234"|sudo tee -a 234
注意,tee 命令的 “-a” 选项的作用等同于 “>>” 命令,如果去除该选项,那么 tee 命令的作用就等同于 “>” 命令
以上两种方法的前提是/etc/sudoers
中对该用户添加了对应命令的权限
11. let
简单的计算器,执行算术表达式。格式,
let arg [arg ...]
参数
arg:算术表达式
返回值
当let最后一个执行的表达式的计算结果为0时返回1,否则返回0。 当let执行的表达式的除数为0时,返回1并报错。
运算符优先级递减表
运算符 | 描述 |
---|---|
id++, id– | 变量后增量、变量后减量 |
++id, --id | 变量预增量、变量预减量 |
-, + | 正号、负号 |
!, ~ | 逻辑否、按位取反 |
** | 幂运算 |
*, /, % | 乘法、除法、取余 |
+, - | 加法、减法 |
>>,<< | 按位左移、右移 |
=, | 比较 |
==, != | 等于、不等于 |
& | 按位与 |
^ | 按位异或 |
| | 按位或 |
&& | 逻辑与 |
|| | 逻辑或 |
expr ? expr : expr | 条件运算符(三元运算符) |
=, *=, /=, %=, +=, -=, >=, &=, ^=, |= | 赋值 |
自增自减
i=1
let i++
let i--
let i=i+20
关于let、expr、[ ]
原生bash不支持简单的数学运算,需通过expr
[root@server ~]# a=1 b=2
[root@server ~]# echo $a $b
1 2
[root@server ~]# echo `expr $a+$b`
1+2
[root@server ~]# echo `expr $a + $b`
3
[root@server ~]# echo `expr $a * $b`
expr: syntax error: unexpected argument ‘123’
[root@server ~]# echo `expr $a \* $b`
2
表达式和运算符之间要有空格,且这里是偏引号
[root@server ~]# echo $[a+b] $a+$b
3 1+2
关于这三个的区别和联系,看达到同样效果,它们各自如何实现:
a=$[1+1]
a=$((1+1))
let a=1+1
a=$(expr 1 + 1 )