Linux九阴真经之无影剑残卷(磁盘存储和文件系统)
Posted huxiaojun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux九阴真经之无影剑残卷(磁盘存储和文件系统)相关的知识,希望对你有一定的参考价值。
一、磁盘简介
计算机的外部存储器中也采用了类似磁带的装置,比较常用的一种叫磁盘,将圆形的磁性盘片装在一个方的密封盒子里,这样做的目的是为了防止磁盘表面划伤,导致数据丢失。
1、硬盘的chs模式
是指chs(Cylinder/Head/Sector)模式,很久以前,硬盘的容量还非常小的时候,人们采用与软盘类似的结构生产硬盘.也就是硬盘盘片的每一条磁道都具有相同扇区数. 由此产生了所谓的3D参数 (Disk Geometry). 既磁头数(Heads),柱面数(Cylinders), 扇区数(Sectors pertrack),以及相应的寻址方式
- sector 扇区:在硬盘上每个磁道上的扇区用6bit标识,每个磁道最多标识64个扇区,每个扇区512byte
- track 磁道:磁道数等于柱面数,每个盘面上有1024个磁道
- head 磁头 :磁头数等于盘面数,在硬盘上磁头用8bit标识,最多寻道256个磁头
- cylinder 柱面 :在硬盘柱面用10bit标识,最多标识1024个柱面
磁盘容量计算方式:扇区大小*64*256*1024=8589934592=8G
因此早期的硬盘无法突破8G容量
以上这种方式会浪费很多磁盘空间 (与软盘一样). 为了解决这一问题, 进一步提高硬盘容量, 人们改用等密度结构生产硬盘.也就是说,外圈磁道的扇区比内圈磁道多. 采用这种结构后, 硬盘不再具有实际的3D参数, 寻址方式也改为线性寻址,即以扇区为单位进行寻址。
2、LBA(logical block addressing)模式
为了与使用3D寻址的老软件兼容 (如使用Bios Int13H接口的软件), 在硬盘控制器内部安装了一个地址翻译器, 由它负责将老式3D参数翻译成新的线性参数.这也是为什么现在硬盘的3D参数可以有多种选择的原因 (不同的工作模式, 对应不同的3D参数,如 LBA, LARGE, NORMAL). CHS模式只能识别大硬盘的前面8G.lba使用的线性寻址,突破了1024柱面的限制,能访问8G以外的空间了。
LBA采用48个bit位寻址,最大寻址空间128PB。
二、硬盘分区
磁盘分区是使用分区编辑器(partition editor)在磁盘上划分几个逻辑部分,盘片一旦划分成数个分区,不同类的目录与文件可以存储进不同的分区。越多分区,也就有更多不同的地方,可以将文件的性质区分得更细,按照更为细分的性质,存储在不同的地方以管理文件;但太多分区就成了麻烦。空间管理、访问许可与目录搜索的方式,依属于安装在分区上的文件系统。当改变大小的能力依属于安装在分区上的文件系统时,需要谨慎地考虑分区的大小。
1、硬盘为什要分区
- 优化I/O性能
- 实现磁盘空间配额限
- 提高修复速度
- 隔离系统和程序
- 安装多个OS
- 采用不同文件系统
2、分区表类型(MBR和GPT)
MBR: Master Boot Record 主引导记录
又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的首个扇区,它在硬盘上的三维地址为(柱面,磁头,扇区)=(0,0,1)。在深入讨论主引导扇区内部结构的时候,有时也将其开头的446字节内容特指为“主引导记录”(MBR),其后是4个16字节的“磁盘分区表”(DPT),以及2字节的结束标志(55AA)。因此,在使用“主引导记录”(MBR)这个术语的时候,需要根据具体情况判断其到底是指整个主引导扇区,还是主引导扇区的前446字节。
mbr分区用4个字节存储分区的总扇区数,最大能表示2的32次方的扇区个数,按每扇区512字节计算(2^32*512byte=2199023255552byte=2T),所以mbr无法识别大于2T以后的空间
0磁道0扇区:512bytes
446bytes:boot loader
64bytes:分区表,16bytes标识一个分区,支持4个分区,或者3个主分区和一个扩展分区
2bytes:分区标示位 55AA
MBR分区结构:
GPT: Globals Unique Identifiers
是一个实体硬盘的分区表的结构布局的标准。它是可扩展固件接口(EFI)标准(被Intel用于替代个人计算机的BIOS)的一部分,被用于替代BIOS系统中的一32bits来存储逻辑块地址和大小信息的主引导记录(MBR)分区表。对于那些扇区为512字节的磁盘,MBR分区表不支持容量大于2.2TB(2.2×1012字节)[1]的分区,然而,一些硬盘制造商(诸如希捷和西部数据)注意到这个局限性,并且将他们的容量较大的磁盘升级到4KB的扇区,这意味着MBR的有效容量上限提升到16 TiB。 这个看似“正确的”解决方案,在临时地降低人们对改进磁盘分配表的需求的同时,也给市场带来关于在有较大的块(block)的设备上从BIOS启动时,如何最佳的划分磁盘分区的困惑。GPT分配64bits给逻辑块地址,因而使得最大分区大小在264-1个扇区成为可能。对于每个扇区大小为512字节的磁盘,那意味着可以有9.4ZB(9.4×1021字节)或8 ZiB个512字节(9,444,732,965,739,290,426,880字节或18,446,744,073,709,551,615(264-1)个扇区×512(29)字节每扇区)
GPT支持128个分区,使用64位表示扇区,如果每个扇区是512Byte,理论上则支持8Z的磁盘。
使用128位UUID(Universally Unique Identifier) 表示磁盘和分区 GPT分区表 自动备份在头和尾两份,并有CRC校验位 UEFI (统一扩展固件接口)硬件支持GPT,使操作系统启动
gpt分区结构:
3、LINUX中的分区管理
LINUX中硬盘及分区工具的标识
在早期系统中硬盘的标识区分接口,比如IED接口的硬盘在/dev/以hd[a-z]标识,新系统中都以sd[a-z]来标识,不区分接口了
分区由数字来标识,比如硬盘是sda,那此硬盘的第一个分区为sda1,第二个为sda2,依次类推
在LINUX中一切皆文件,设备是以文件的方式标识,设备有主设备号和次设备号,主设备号表示不同类型的设备,次设备号表示同类设备的不同设备
LINUX中管理分区的工具
fdisk /dev/DISK
- -l:列出分区信息
- -c:使用非柱面模式
- 交互式:
- p:打印当前分区表
- m:帮助
- n:添加新分区
- d:删除分区
- w:保存退出
- l:列出分区类型
- t:修改分区类型
- q 不保存并退出
示例: [[email protected] ~]# fdisk /dev/sdb Command (m for help): p #查看当前分区 Disk /dev/sdb: 2147 MB, 2147483648 bytes 255 heads, 63 sectors/track, 261 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0xb484790b Device Boot Start End Blocks Id System Command (m for help): n #新创建一个分区 Command action e extended p primary partition (1-4) p #创建主分区 Partition number (1-4): 1 #分区号为1 First cylinder (1-261, default 1): #默认从0扇区开始 Using default value 1 Last cylinder, +cylinders or +size{K,M,G} (1-261, default 261): +1G #创建一个1G的分区 Command (m for help): p #查看分区后的状态 Disk /dev/sdb: 2147 MB, 2147483648 bytes 255 heads, 63 sectors/track, 261 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0xb484790b Device Boot Start End Blocks Id System /dev/sdb1 1 132 1060258+ 83 Linux Command (m for help): w #保存退出 The partition table has been altered! Calling ioctl() to re-read partition table. Syncing disks.
gdisk:用来管理gpt分区,用法和fdisk相似
parted /dev/DISK
- mklabel {gpt|msdos} 设置硬盘的分区表类型,也可以进入交互式模式
- -l:列出分区信息
- 交互式
cp [FROM-DEVICE] FROM-NUMBER TO-NUMBER 将文件系统复制到另一个分区 help [COMMAND] 打印一般帮助,或帮助COMMAND mklabel,mktable LABEL-TYPE 创建一个新的disklabel(分区表),LABEL-TYPE={msdos|gpt} mkpart PART-TYPE [FS-TYPE] START END 新建一个分区 mkpartfs PART-TYPE FS-TYPE START END 新建一个带文件系统的分区 quit 退出程序 resize NUMBER START END 调整分区NUMBER及其文件系统的大小 rm NUMBER 删除NUMBER分区 version 显示GNU Parted的版本号和版权信息
partprobe 同步分区表,但在红帽系列6.x上有bug
partx
- -a /dev/sda 同步分区表(6.x),在增加分区出现不同步时使用
- -d --nr m-n /dev/sda 同步分区表(6.x),在删除分区出现不同步时使用
lsblk 查看内存现在生效的分区表
4、mbr分区表的备份与还原
[[email protected] ~]# dd if=/dev/sda of=/data/mbr_bak bs=1 count=512 #备份mbr分区表 512+0 records in 512+0 records out 512 bytes (512 B) copied, 0.000765317 s, 669 kB/s [[email protected] ~]# hexdump -C /data/mbr_bak 00000000 eb 48 90 10 8e d0 bc 00 b0 b8 00 00 8e d8 8e c0 |.H..............| 00000010 fb be 00 7c bf 00 06 b9 00 02 f3 a4 ea 21 06 00 |...|.........!..| 00000020 00 be be 07 38 04 75 0b 83 c6 10 81 fe fe 07 75 |....8.u........u| 00000030 f3 eb 16 b4 02 b0 01 bb 00 7c b2 80 8a 74 03 02 |.........|...t..| 00000040 80 00 00 80 fe 49 08 00 00 08 fa 90 90 f6 c2 80 |.....I..........| 00000050 75 02 b2 80 ea 59 7c 00 00 31 c0 8e d8 8e d0 bc |u....Y|..1......| 00000060 00 20 fb a0 40 7c 3c ff 74 02 88 c2 52 f6 c2 80 |. [email protected]|<.t...R...| 00000070 74 54 b4 41 bb aa 55 cd 13 5a 52 72 49 81 fb 55 |tT.A..U..ZRrI..U| 00000080 aa 75 43 a0 41 7c 84 c0 75 05 83 e1 01 74 37 66 |.uC.A|..u....t7f| 00000090 8b 4c 10 be 05 7c c6 44 ff 01 66 8b 1e 44 7c c7 |.L...|.D..f..D|.| 000000a0 04 10 00 c7 44 02 01 00 66 89 5c 08 c7 44 06 00 |....D...f.\\..D..| 000000b0 70 66 31 c0 89 44 04 66 89 44 0c b4 42 cd 13 72 |pf1..D.f.D..B..r| 000000c0 05 bb 00 70 eb 7d b4 08 cd 13 73 0a f6 c2 80 0f |...p.}....s.....| 000000d0 84 f0 00 e9 8d 00 be 05 7c c6 44 ff 00 66 31 c0 |........|.D..f1.| 000000e0 88 f0 40 66 89 44 04 31 d2 88 ca c1 e2 02 88 e8 |[email protected]| 000000f0 88 f4 40 89 44 08 31 c0 88 d0 c0 e8 02 66 89 04 |[email protected]| 00000100 66 a1 44 7c 66 31 d2 66 f7 34 88 54 0a 66 31 d2 |f.D|f1.f.4.T.f1.| 00000110 66 f7 74 04 88 54 0b 89 44 0c 3b 44 08 7d 3c 8a |f.t..T..D.;D.}<.| 00000120 54 0d c0 e2 06 8a 4c 0a fe c1 08 d1 8a 6c 0c 5a |T.....L......l.Z| 00000130 8a 74 0b bb 00 70 8e c3 31 db b8 01 02 cd 13 72 |.t...p..1......r| 00000140 2a 8c c3 8e 06 48 7c 60 1e b9 00 01 8e db 31 f6 |*....H|`......1.| 00000150 31 ff fc f3 a5 1f 61 ff 26 42 7c be 7f 7d e8 40 |1.....a.&B|..}[email protected]| 00000160 00 eb 0e be 84 7d e8 38 00 eb 06 be 8e 7d e8 30 |.....}.8.....}.0| 00000170 00 be 93 7d e8 2a 00 eb fe 47 52 55 42 20 00 47 |...}.*...GRUB .G| 00000180 65 6f 6d 00 48 61 72 64 20 44 69 73 6b 00 52 65 |eom.Hard Disk.Re| 00000190 61 64 00 20 45 72 72 6f 72 00 bb 01 00 b4 0e cd |ad. Error.......| 000001a0 10 ac 3c 00 75 f4 c3 00 00 00 00 00 00 00 00 00 |..<.u...........| 000001b0 00 00 00 00 00 00 00 00 04 bc 00 00 00 00 80 20 |............... | 000001c0 21 00 83 dd 1e 3f 00 08 00 00 00 a0 0f 00 00 dd |!....?..........| 000001d0 1f 3f 83 fe ff ff 00 a8 0f 00 00 00 80 02 00 fe |.?..............| 000001e0 ff ff 83 fe ff ff 00 a8 8f 02 00 00 40 01 00 fe |[email protected]| 000001f0 ff ff 05 fe ff ff 00 a8 cf 03 00 58 30 01 55 aa |...........X0.U.| 00000200 [[email protected] ~]# dd if=/data/mbr_bak of=/dev/sda bs=1 count=512 #如果分区表损坏了可以进入rescue模式进行从新还原分区表,前提是分区表备份不要在这块硬盘上
gpt分区表会自动在磁盘尾扇区镜像备份一份
三、文件系统
文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于NAND Flash的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
[[email protected] ~]# ls /lib/modules/2.6.32-642.el6.x86_64/kernel/fs/ 列出系统支持的文件系统 autofs4 cramfs ext3 gfs2 mbcache.ko squashfs btrfs dlm ext4 jbd nfs ubifs cachefiles ecryptfs fat jbd2 nfs_common udf cifs exportfs fscache jffs2 nfsd xfs configfs ext2 fuse lockd nls
Linux文件系统:ext2(Extended file system), ext3, ext4, xfs(SGI), btrfs(Oracle), reiserfs, jfs(AIX), swap 光盘:iso9660 Windows:FAT32, exFAT,NTFS Unix: FFS(fast), UFS(unix), JFS2 网络文件系统:NFS, CIFS 集群文件系统:GFS2, OCFS2(oracle) 分布式文件系统: fastdfs,ceph, moosefs, mogilefs, glusterfs, Lustre RAW:未经处理或者未经格式化产生的文件系统
Linux的虚拟文件系统:VFS
由于底层文件系统类型太多,从而导致程序调用复杂,从而有VFS来作为一个统一接口,充当中间人的角色
super block 和 inode table 的关系
1、+文件系统管理工具+
mkfs.{ext4|ext3|xfs|...} 格式化指定设备分区
- -t FSTYPE:格式化指定设备分区
- -L:设定卷标
- -b:块大小
- -m #:预留%
- -i:为多少个字节创建一个节点号
- -N:指定节点号
findfs LABEL=<label>|UUID=<uuid>:查找分区
blkid:查看分区属性信息
- -U UUID
- -L LABEL
fsck: 文件系统检查,FS_TYPE一定要与分区上已经文件类型相同,修复时分区一定要在非挂载状态
- fsck.FS_TYPE
- fsck -t FS_TYPE
- -p: 自动修复错误
- -r: 交互式修复错误
- -y:自动回答为yes
- -f:强制修复
xfs_info:查看xfs文件系统的信息
xfs_growfs 挂载点 同步调整大小后文件系统,适用xfs
+ext系列专用管理工具+
mke2fs:格式化ext系列的文件系统
- -t {ext2|ext3|ext4} 指定文件系统类型
- -b {1024|2048|4096} 指定块大小
- -L 卷标 设置卷标
- -j == mkfs.ext3
- -i # 为数据空间中每多少个字节创建一个inode;此大小不应该小于block的大小
- -N #:指定分区中创建多少个inode
- -l # 一个inode记录占用的磁盘空间大小,128---4096
- -m #: 默认5%,为管理人员预留空间占总空间的百分比
- -o FEATURE[...]:启用指定特性
- -o ^FEATURE:关闭指定特性
e2label device [newlabel]:管理ext系列文件系统的LABEL
tune2fs:重新设定ext系列文件系统可调整参数的值
- -l:查看指定文件系统超级块信息;super block
- -L ‘LABEL‘:修改卷标
- -m #:修预留给管理员的空间百分比
- -j: 将ext2升级为ext3
- -O: 文件系统属性启用或禁用, –O ^has_journal
- -o: 调整文件系统的默认挂载选项,–o ^acl
- -U UUID: 修改UUID号
dumpe2fs 查看超级块信息,显示分组
-h:查看超级块信息,不显示分组信息
e2fsck:ext系列文件专用的检测修复工具
resize2fs 设备 同步调整大小后文件系统,适用ext系列
2、+设备挂载工具+
挂载是将额外文件系统与根文件系统某现存的目录建立起关联关系,进而使得此目录做为其它文件访问入口的行为,挂载点下原有文件在挂载完成后会被临时隐藏
卸载是为解除此关联关系的过程,要在此设备不在有进程使用时才可以正常卸载
[[email protected] ~]# cat /etc/mtab #查看当前已挂载的设备 /dev/sda2 / ext4 rw 0 0 proc /proc proc rw 0 0 sysfs /sys sysfs rw 0 0 devpts /dev/pts devpts rw,gid=5,mode=620 0 0 tmpfs /dev/shm tmpfs rw 0 0 /dev/sda1 /boot ext4 rw 0 0 /dev/sda3 /data ext4 rw 0 0 none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
/etc/mtab 每次挂载时都更新次文件
cat /proc/mounts 可以查看挂载信息
/etc/fstab 开机自动挂载
# /etc/fstab # Created by anaconda on Fri Apr 20 22:56:45 2018 # # Accessible filesystems, by reference, are maintained under ‘/dev/disk‘ # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info # UUID=604bb620-06b7-496e-810d-373a06749b31 / xfs defaults 0 0 UUID=635b125a-667b-4f64-b576-c12290fdbcf5 /boot xfs defaults 0 0 UUID=564c85be-1a0d-4851-a544-1ddde8ba7cd8 /data xfs defaults 0 0 UUID=2fea38ae-b8cf-4fec-99a3-d1b7e13324ff swap swap defaults 0 0 #挂载设备 挂载点 文件系统类型 挂载选项 备份时间间隔(天) 开机检查文件系统(0:不检查,1-n:按数字从小到大依次检查,参数相等的并行检查)
# vim /etc/grub.conf kernel - max_loop=### 增加loop设备数,6.x上默认8个,7.x不限
mount
用法:mount 设备 挂载点
- LABEL=卷标名 挂载点
- UUID=‘xxxxxx’ 挂载点
- -r 只读方式挂载
- -w 读写挂载,默认
- -n 不更新/etc/mtab
- -t 指定文件系统,一般不用,系统可以自动识别
- -a 自动挂载/etc/fstab中的设备
- -B | --bind 目录挂载到目录
- -o:选项
- acl:启用acl
- noacl:关闭acl
- remount:重新挂载
- ro:只读
- async:异步模式
- sync:同步模式
- loop:可以挂载文件,loop设备
- atime/noatime:包含目录和文件
- diratime/nodiratime:目录的访问时间戳
- auto/noauto:是否支持自动挂载,是否支持-a选项
- exec/noexec:是否支持将文件系统上运行应用程序
- dev/nodev:是否支持在此文件系统上使用设备文件
- suid/nosuid:是否支持suid和sgid权限
- user/nouser:是否允许普通用户挂载此设备,/etc/fstab使用
- defaults:相当于rw, suid, dev, exec, auto, nouser, async
- nouuid:不检查UUID的冲突
umount {设备名|挂载点} 卸载设备
fuser
- -v /dev/sda1 查看设备谁在用
- -km /dev/sda1杀掉使用设备的进程
lsof /dev/sda1 查看设备那个进程在用
findmnt 查找某个文件夹是否处于挂载状态
losetup -a 查看当前挂载的loop设备
losetup /dev/loop# loopfile 绑定文件到loop设备
3、+内存及swap管理工具+
free 查看内存和swap的信息
- -m 以M为单位显示
- -g 以G为单位显示
- -h 以人类可以看懂的方式显示
mkswap /dev/sdb1 -L swap_sdb1 格式化
#vi /etc/fstab UUID=9e183975-5adf-41cb-886e-feef62a81335 swap swap defaults,pri=# 0 0 swap的UUID 挂载点 挂载文件系统类型 挂载选项,优先级:数字越大越优先 0 0
swapon DEV|LOOPFILE 激活swap分区
- -s 查看当前的swap信息
- -a 激活fstab表中配置的swap
swapoff 禁用swap分区
4、-其他块设备操作工具-
eject 弹出光驱
- -t 弹入光驱
cp /dev/sr0 /data/centos7.iso 将光盘制作成iso文件
dd if=/dev/sr0 of=/data/centos7.iso 将光盘制作成iso文件
mkisofs -r -o etc.iso /etc/ 将目录打包成iso文件,但不能引导
mkdvdiso.sh source /destination/DVD.iso 制作引导光盘
#!/bin/bash
# by Chris Kloiber <[email protected]>
# Mods under CentOS by Phil Schaffner <[email protected]>
# A quick hack that will create a bootable DVD iso of a Red Hat Linux
# Distribution. Feed it either a directory containing the downloaded
# iso files of a distribution, or point it at a directory containing
# the "RedHat", "isolinux", and "images" directories.
# This version only works with "isolinux" based Red Hat Linux versions.
# Lots of disk space required to work, 3X the distribution size at least.
# GPL version 2 applies. No warranties, yadda, yadda. Have fun.
# Modified to add sanity checks and fix CentOS4 syntax errors
# TODO:
# Add checks for available disk space on devices holding output and
# temp files.
# Add optional 3rd parameter to specify location of temp directory.
# Create .discinfo if not present.
OS_VER=$((test -e /etc/fedora-release && rpm -qf /etc/fedora-release --qf "FC%{VERSION}") || (test -e /etc/redhat-release && rpm -qf /etc/redhat-release --qf "EL%{VERSION}") || echo OS_unknown)
case "$OS_VER" in
EL[45]*|FC?)
IMPLANT=/usr/lib/anaconda-runtime/implantisomd5
if [ ! -f $IMPLANT ]; then
echo "Error: $IMPLANT Not Found!"
echo "Please install anaconda-runtime and try again."
exit 1
fi
;;
EL6*|FC1?)
IMPLANT=/usr/bin/implantisomd5
if [ ! -f $IMPLANT ]; then
echo "Error: $IMPLANT Not Found!"
echo "Please install isomd5sum and try again."
exit 1
fi
;;
OS_unknown)
echo "Unknown OS."
exit 1
;;
*)
echo "Fix this script for $OS_VER"
exit 1
esac
if [ $# -lt 2 ]; then
echo "Usage: `basename $0` source /destination/DVD.iso"
echo ""
echo " The ‘source‘ can be either a directory containing a single"
echo " set of isos, or an exploded tree like an ftp site."
exit 1
fi
DVD_DIR=`dirname $2`
DVD_FILE=`basename $2`
echo "DVD directory is $DVD_DIR"
echo "ISO file is $DVD_FILE"
if [ "$DVD_DIR" = "." ]; then
echo "Destinaton Directory $DVD_DIR does not exist"
exit 1
else
if [ ! -d "/$DVD_DIR" ]; then
echo "Destinaton Directory $DVD_DIR must be an absolute path"
exit 1
else
if [ "$DVD_FILE" = "" ] || [ -d "$DVD_DIR/$DVD_FILE" ]; then
echo "Null ISO file name."
exit 1
fi
fi
fi
which mkisofs >&/dev/null
if [ "$?