-
inodes
inodes就是索引节点(Index Node)的意思,在Linux中,所有的file的相关信息(不包括filename和它实际本身的数据)都被存储在一个表结构中,这个表结构通常存储在当前分区的开始位置,每个分区都有自己的inode table。inode中保存信息如下:
- Size
- Permission
- Owner/Group
- Location of the hard drive(硬盘)
- Date/time
- Other information
文件夹中保存的是file信息的entry,包括filename和inode number,当我们获取file信息的时候都是通过唯一的inode number去inode table中获取相应信息的。
#查看inode number(打印行最前面的就是) ls -i #查看所有分区的inode使用情况 df -hi
所以如果你遇到了系统提示“磁盘空间不足”问题,但是实际查看后发现还有空间时,这就是由于inodes用完了的原因。但是通常你不会遇到这个问题,因为inodes数量在创建文件系统的时候就决定了,默认的配置是每2K个字节的空间就会创建一个inode,也就是说所有文件的平均大小如果超过2K字节则不会遇到inodes先耗尽的情况,反之则会遇到,2K太小了,在实际情况中普通用户大概率不会创建如此多的小文件来满足这个条件,所以通常在遇到inodes耗尽的问题之前磁盘空间往往会先被耗尽。这种情况在邮件系统中可能会遇到,因为很多邮件通常是不足2K字节的小文件。当然在创建文件系统时你也可以配置创建多少inodes。
hard link和其链接的file的inode number是一样的,而soft link(symbolic link)则是不同的,因此hard link不能在不同分区和文件系统之间使用,因为这会造成inode number冲突。
inodes也是系统更新后可以不用强制重启也能进行的原因,因为更新的系统文件都是新创建的,它会有新的inode,和旧的不冲突,因此在当前运行状态中使用的旧inodes不会被影响,至于在重启时会不会删除旧的同义文件那就要看具体系统的实现了。
另外,在一些文件系统上(比如ext4),支持将文件的实际data也存储在inode上,这会充分利用inode的剩余空间从而不再需要data block来达到节省磁盘空间的目的,但同时,它也会增加inode的查找时间,而且限于inode本身的大小它只能保存很少的数据。
-
Symbolic Link(Soft Link)
软连接相当于Windows上的快捷方式,它是一个指向源文件的别名文件,相当于代码层面的指针一样,使用它操作文件内容也会作用在真实源文件上,但是删除它是不会删除源文件的,相当于删除了一个可操作源文件的引用一样。
需要注意的是,软连接的权限都是777的,但并不意味着源文件的权限。如果使用chmod命令修改软链接权限则修改的其实是源文件的权限,软连接本身的权限可以忽视。
#创建symbolic link ln -s target_file link_name #创建hard link ln target_file link_name
如果没指定-s则是创建hard link。
如果想要查看symbolic link指向的源文件:
#最简单的方式,相当于readlink -f realpath link_name #这种方式对于link指向link的情况是无法查看到源文件的,需要加上“-f”选项才行 readlink -f link_name #当然,使用ls -l也可以,但是这种方式对于link指向link的情况是无法查看到源文件的 ls -l file #使用“-c%N”可以过滤掉其他信息 stat -c%N link_name #file命令 file link_name
删除link:
rm link_name1 link_name2 ...
也可以使用unlink命令删除link,unlink也可以删除文件(但不能删除文件夹),一次运行只能删除一个文件,
删除的原理就是删掉所属文件夹中的entry,并且将inode中的link数量减1,如果link数为0了内核就会删掉这个inode并清除其data block(磁盘中的文件内容)
。软连接可以指向一个不存在的文件,因此如果软连接指向的源文件被删除了,则软连接就没用了,继续存在只会占用inode数量和内存,通常需要删除它们,怎么找到它们呢:
find /path/ -xtype l #还可以使用symlinks命令(需要安装) symlinks -r directory //-r表示遍历 symlinks -d directory //-d表示找到并删除
-
Hard Link
不同于软连接,hard link指向源文件的inode,如果存在一个hard link,则删除源文件的file并不会导致它的实际data被删除,只是移除了其所在directory中的entry,其inode中的link数量减1,但由于还有一个hard link,所以inode中的link数量并不为0,所以内核并不会删除其data block。
Hard Link文件和源文件除了设置的名字、本身操作的时间和所在的位置不同之外,信息完全一样,这还是取决于是你自己创建的Hard Link文件,如果是阅读别人的文件系统那你完全分辨不出来哪个是源文件,所以Hard Link一旦建立,可以理解成存在了两个完全相同的源文件,他不是简单的拷贝,而是都指向同一块数据,你可以理解成Java中的对象的浅复制。我想Hard Link应该就是实现回收站功能的原理。
-
ls -l命令的打印信息
ls -l #print: -rwxrw-r-- 1 abhi itsfoss 457 Aug 10 11:55 demo.txt
File Type:最开始的一个字符表示文件类型,“-”表示文件,“d”表示文件夹,“l”表示symbolic link,注意hard link的类型也是“-”而不是“l”。
-
Permissions:从第二个字符开始的就是权限信息,有9个字符,每三个为一组,从左开始第一组为文件拥有者(User)的权限,第二组表示Filename的权限,第三组表示所有用户的权限。
- r : Read permission
- w : Write permission
- x : Execute permission
- – : No permission set,表示不能读也不能写也不能执行。
read、write、execute对于文件和文件夹的意义:
Permissions for files
- Read – Can view or copy file contents
- Write – Can modify file content
- Execute – Can run the file (if its executable)
Permissions for directories
- Read – Can list all files and copy the files from directory
- Write – Can add or delete files into directory (needs execute permission as well)
- Execute – Can enter the directory
Hard link count:即inode的连接数,如果这个值为0了则就会被系统删除其数据块。
User:文件的拥有者,就是上面的“abhi”。
Group:用户所属的组,就是上面的“itsfoss”。
File size:文件大小,就是上面的“457”。
Modification time:文件修改时间,就是上面的“Aug 10 11:55”。
Filename:文件名。
可以使用chmod命令修改文件权限,有两种方式,一种是:
chmod 666 fine_name #表示为: rw-rw-rw-
“666”的每个数字都表示一组权限:
- r (read) = 4
- w (write) = 2
- x (execute) = 1
- – (no permission) = 0
因此上面的“6”可以拆成4+2,因此他表示read权限和write权限。
还有一种方式是使用字母表示:
chmod g=x,u+r,o-w file_name #表示为: =表示覆盖,上述g=x表示group权限为--x -表示移除某个权限,上述o-w表示其他所有用户的权限中移除write权限 +表示添加某个权限,上述u+r表示User的权限中加上read权限
u = user owner
g = group owner
o = other
a = all (user + group + other)
+ for adding permissions
– for removing permissions
= for overriding existing permissions with new value
如果想要修改文件所属用户或者用户组:
#修改用户 chown
#修改用户和组 chown : #只修改用户组 chown : #你可以用chgrp修改组 chgrp 除了rwx之外,还有s和t两种权限,这两种权限只能出现在执行权限(也就是x)的位置,s权限表示拥有root的执行权限,可以不用使用sudo标志即可执行,它只能出现在User或Group的x位置。t作用于文件夹,表示只能被删除或者重命名,它只能存在于Other的x位置上:
#s和t在有的linux系统上是大写的 drwsrwsr-t 2 linuxhandbook linuxhandbook 4096 Apr 12 19:55 my_dir/
-
运行规则
如果需要一次运行多个命令:
#如果第一条命令运行成功了则接着运行第二条命令,否则只会运行第一条 apt update && apt upgrade #如果第一条命令运行失败了则接着运行第二条命令,否则只会运行第一条 apt update || apt upgrade #运行第一条,然后运行第二条 apt update ; apt upgrade
-
Bash Script语法
#如果当前目录下有一个my_script.sh脚本 bash my_script.sh #也可以这样执行 ./my_script.sh #如果提示Permission denied,则需要添加执行权限: chmod u+x my_script.sh #现在你可以执行./my_script.sh了
-
定义变量
#等号两边不能有空格 var=value #比如 message="Hello World" echo $message
-
给脚本传递命令行参数
./my_script.sh arg1 arg2 ... #比如 ./my_script.sh A 100 #my_script.sh中接收: echo "Script name is: $0" echo "First arg is: $1" echo "Second arg is: $2"
$0 一定会有,是运行脚本命令的名字,比如上述例子的话就是“./my_script.sh”,如果使用bash my_script.sh运行的话, $0 就是my_script.sh。
除了按照数字索引之外还有其他的变量引用方式:
Special Variable Description $0 Script name $1, $2...$9 Script arguments(传入参数从1开始算,因为$0是脚本名) ${n} Script arguments from 10 to 255,比如第15个参数:${15} $# Number of arguments (不包括$0) $@ All arguments together(不包括$0,结果是一个集合,多参数对应多个元素) $$ Process id of the current shell (当前的进程id,你可以是使用.或source命令测试,子进程执行脚本的话每次结果都不一样) $! Process id of the last executed command(上一个执行命令的进程id) $? Exit status of last executed command(上一个命令执行的结果码) $* All arguments together(同样不包含$0,没有双引号包括的情况下和$@的结果是一样的;在有双引号包括的情况下结果只有一个元素,每个参数用空格隔开,可以使用循环测试它们的输出结果) 和键盘交互,读取键盘输入:
echo "What's your name?" read name #会间接创建变量name,键盘输入的值会赋到name上 read -p "How old are u, $name?" age #-p可以输出一句提示语 echo "Ok, $name is $age."
-
数学运算
Operator Description + Addition - Subtraction * Multiplication / Integer division (without decimal) % Modulus division (only remainder) 取余 ** Exponentiation (a to the power b) 幂运算 sum=$(($num1/$num2)) #如果num1是10,num2是3,则sum的结果是3,因为默认的只支持整型,如果想要结果是精确的浮点型: result=$(echo "$num1/$num2" | bc -l) #$()是命令替换语法,在其中可以执行其他命令
-
数组
array_name=(item1 item2 item3 ..) #获取元素 ${array_name[N]} #获取所有元素 ${array_name[*]} #获取数组长度 ${#array_name[@]}
-
字符串
#获取字符串长度 ${#string} #拼接字符串 str3=$str1$str2 #截取一定长度的字符串,pos是开始下标,len是要截取的字符个数 ${string:$pos:$len} #替换字符串内容 ${string/subStr1/newSubStr} #删减字符串 ${string/deleteStr}
-
if语句
#注意if和[之间的空格、[]之间和表达式之间的空格、then和分号之间的空格(有的系统可以没有)都是必须的 #表达式中的比较符和变量之间同样也需要空格 if [ expression ]; then ## execute this block if condition is true else go to next elif [ expression ]; then ## execute this block if condition is true else go to next else ## if none of the above conditions are true, execute this block fi #if结束语句
Condition Equivalent to true when $a -lt $b b ($a is less than $b) $a -gt $b b ($a is greater than $b) $a -le $b b ($a is less or equal than $b) $a -ge $b b ($a is greater or equal than $b) $a -eq $b $a is equal to $b $a -ne $b $a is not equal to $b 对于字符串,可以这样比较(注意空格需要):
Condition Equivalent to true when "$a" = "$b" $a is same as $b "$a" == "$b" $a is same as $b "$a" != "$b" $a is different from $b -z "$a" $a is empty 文件类型判断(注意空格需要):
Condition Equivalent to true when -f $a $a is a file -d $a $a is a directory -L $a $a is a link if语句后面的条件语句也可以不用[]括起来。
-
循环语句
;(分号)是区分换行的意思,下面的例子,如果do放在单独一行的话则不需要加“;”。#!/bin/bash #"in words"可以缺省,如果缺省的话表示循环所有的命令行参数(positional parameter) for num in {1..10}; do echo $num done #同样可以作用于数组 for item in ${array_name[*]}; do echo $item done
#!/bin/bash num=1 while [ $num -le 10 ]; do echo $num num=$(($num+1)) done
#!/bin/bash num=1 until [ $num -gt 10 ]; do echo $num num=$(($num+1)) done
-
函数
function_name() { commands }
比如:
#!/bin/bash sum() { sum=$(($1+$2)) echo "The sum of $1 and $2 is: $sum" } echo "Let's use the sum function" #sum函数传入的参数个数不能少于比sum中$引入的下标最大值,但是可以超出 sum 1 5
-
命令替换
当需要在脚本中执行其他命令时:
#方式1 $(command expression) #方式2 `command expression` #比如获取时间戳: `date` #也可以放在双引号中调用 echo "`date`"
-
select语句
select用于让用户在一组数据中选取某一项然后执行某些操作array=(A B C) #REPLY是内置变量,会存储选中项的行标 select var in ${array[*]}; do echo you picked $var \($REPLY\) done #输出为: 1)A 2)B 3)C #如果当你键盘输入对应的数字2按回车后输出: you picked B (2)
-
表达式匹配判断
使用[[...]]来进行表达式的判断:#按照Local字符排序进行比较 [[ $str < "ABC"]] #模糊匹配 [[ $str =~ [a-z]*(a)+(bf)?c ]] #上述规则表示匹配至少有1个a、1个c的字符串 #通配符表示:*(0个或多个)、+(1个或多个)、?(0个或1个)、@(1个)、!(排除) #如上所述,字符集必须用[]括起来,此外,POSIX提供了一些标准字符集类,比如word可以匹配字母、数字和_,使用规则如下: [[ $str =~ [[:word:]]*(a)+(bf)?c ]] #此外,还有space、digit、upper等字符集类
-
case语句
示例:echo -n "Enter the name of an animal: " read ANIMAL echo -n "The $ANIMAL has " case $ANIMAL in horse | dog | cat) echo -n "four";; man | kangaroo ) echo -n "two";; *) echo -n "an unknown number of";; esac echo " legs."
case的每条语句前面可以加(,也可以省略,比如:
case $ANIMAL in (horse) echo -n "four";; esac
末尾的esac是case倒过来的拼写,表示case块的结束。
-
条件表达式中的一些action
条件表达式中除了-lt、-eq这些之外还有很多action,比如:#True if file exists. -a file -e file #True if file exists and is a directory. -d file #True if file exists and is a regular file. -f file #True if file exists and is a symbolic link. -h file #True if file exists and is readable. -r file #True if file exists and is writable. -w file #True if file exists and is executable. -x file
其他的可在手册中的Bash Conditional Expressions部分查找。
-
Bourne Shell Builtins
.表示在当前shell进程执行,比如./script.sh是开启一个子进程执行,而. script.sh是在当前shell进程执行,这会导致设定的局部变量在执行完命令后仍然有效。source命令也是同样的作用。
:表示空语句,比如case和if中分支中什么也不做的时候用它。
eval使用:
eval会将参数中的扫描执行其中的命令,然后对执行完命令后输出的结果再次扫描,扫描到命令后再次执行,返回结果。
比如:Mac-mini:Desktop hardy$ set 11 22 33 Mac-mini:Desktop hardy$ echo $3 33 #第一个$被引号括起来成为了一个字符,第二个$是命令操作符,结合#打印出set中设置的变量个数,因此输出$3 Mac-mini:Desktop hardy$ echo '$'$# $3 #使用eval之后,第一遍扫描会输出$3,这个结果会存到eval中,然后再次执行上次的结果$3就会输出第三个变量33 Mac-mini:Desktop hardy$ eval echo '$'$# 33
exec后面跟着的命令会替换当前的shell进程,执行完后会退出(exec之后还有命令也不执行了),所以一般用在脚本文件中。
-
basename命令
basename可以过滤出脚本文件名,比如:#!/bin/bash echo $0 Mac-mini:Desktop hardy$ /path/script.sh /path/script.sh #使用basename可以把前面的path去掉 echo `basename "$0"` #输出: script.sh
-
dirname
dirname和basename相反,它会截取出目录:Mac-mini:Desktop hardy$ dirname /User/mph/script.sh #输出: /User/mph/
-
expr
expr expr1 : expr2表示判断expr1是否是expr2的格式,expr2是正则表达式。比如:#'\('表示”(“,'\)'表示”)“,$表示匹配结果字符,”\(cd\)$“表示匹配结尾是cd的字符串 if expr "/abcd" : '/.*\(cd\)$' > /dev/null;then echo "True" else echo "False" fi #/dev/null表示空字符串
关于其返回值,在匹配失败的时候,如果expr2不为空,则返回null string,这个值和/dev/null相等;如果expr2不为空则返回0。在匹配成功的时候也有两种情况,如果expr2内没有"()"匹配项则返回已匹配上的字符个数,如果expr2内有“()”匹配项则返回
第一个
"()"匹配项匹配上的子字符串。 pwd -L和-P
-L显示逻辑路径,-P显示物理路径。默认是-L。
-
-
/dev/null
/dev/null在linux中表示一个虚拟设备,只能写入不能读取,主要用来重定向输出流(>或者1>)或错误流(2>),比如:
#cd到一个不存在的文件夹时会报错,错误信息默认打印在terminal中:-bash: cd: aa: No such file or directory cd non_exist_directory #但是如果: cd non_exist_directory 2>/dev/null #此时,命令行不会看到错误信息,因为错误流被重定向到/dev/null了 #同样,>/dev/null或1>/dev/null的意思是不显示正确信息,比如pwd pwd >/dev/null
-
2>&1
表示将错误输出流重定向到标准输出流,不能写成2>1,这就变成了重定向到名叫“1”的文件,示例:
>/dev/null 2>&1 #表示标准输出流重定向到/dev/null,然后错误输出流重定向到标准输出流,所以结果相当于标准输出流、错误输出流都重定向到/dev/null
-
ulimit
Linux中用于shell启动进程所占用的资源设置,常用option如下:
-H 设置硬资源限制.
-S 设置软资源限制.
-a 显示当前所有的资源限制.
-c size:设置core文件的最大值.单位:blocks
-d size:设置数据段的最大值.单位:kbytes
-f size:设置创建文件的最大值.单位:blocks
-l size:设置在内存中锁定进程的最大值.单位:kbytes
-m size:设置可以使用的常驻内存的最大值.单位:kbytes
-n size:设置内核可以同时打开的文件描述符的最大值.单位:n
-p size:设置管道缓冲区的最大值.单位:kbytes
-s size:设置堆栈的最大值.单位:kbytes
-t size:设置CPU使用时间的最大上限.单位:seconds
-v size:设置虚拟内存的最大值.单位:kbytes
-u <程序数目> 用户最多可开启的程序数目ulimit -H -n #表示查询默认的该进程可打开的最大文件描述符数量,如果无法查询会返回非0code ulimit -n unlimited #表示无限制(通常是默认配置)