shell脚本
介绍
shell是一个命令解释器,它的作用是解释执行用户输入的命令或者程序
- 交互式的方式进行执行
- 非交互式的方式进行执行,通过shell脚本来执行
shell脚本的类型
shell脚本语言是弱类型语言,无需定义变量类型即可使用,Linux下主要有两种类型的shell语言
- Bourne shell 包括Bourne shell(sh)、Korn shell(ksh)、Bourne again shell
- C shell 包括csh, tcsh两种类型
查看系统默认的shell
echo $SHELL
查看系统支持的shell类型
cat /etc/shells
shell脚本执行
shell脚本的执行通常可以按照以下几种方式进行:
- bash filename / sh filename 这种方式适用于脚本文件本身没有执行权限的时候
- /path/filename 这种方式适用于脚本文件本身具有执行权限,则可以直接执行
- source filename / .filename 通过这种方式读入和加载shell脚本文件,然后依次执行shell脚本中的语句,有区别的是,这些语句是在当前父shell脚本的进程中执行。其他两种方法都会启用新的进程执行该脚本。
shell脚本编写规范
遵循以下shell脚本规范,在提高开发效率的同时,降低后期的维护成本
- 一个规范的shell脚本应该在第一行指出应该由哪个解释器来执行shell脚本中的内容,指定的方式为
#! /bin/bash
或者#! /bin/sh
, sh为bash的软连接,因此两者没有区别 - ‘#’后面的内容表示注释内容,注释内容不会被执行,也不会被解释器看到
- shell脚本的开头加版本,版权等信息
- shell脚本文件的扩展名为 .sh
- 成对的符号应尽量一次性写出来,然后退格在符号内增加内容,以防止遗漏。这些成对的符号包括: {}、[]、‘’、“” 等
- 中括号[]两端至少要有1个空格,因此,键入中括号时即留出空格[ ],然后在退格键入中间内容,并确保两端都至少由一个空格
- 对于流程控制语句,应一次性将格式写完,再添加内容
变量
-
变量的赋值方法为: 先写变量名称,紧接着是= ,最后是值。中间无任何空格。 通过echo命令加上 $变量名,即可输出变量的值
-
交互式赋值方法,将外部输入读取到变量中:
read -p "Please input option: " Opt echo The user input is: $Opt
-
赋值时使用引号
- 双引号 : 允许通过$符号引用其他变量的值
- 单引号 : 不允许使用$符号引用其变量的值
- 反引号 : 提取命令执行后的输出结果
- $() 与 ``含义相同
#! /bin/bash A=10 echo $A # 无引号 B=$A+10 echo $B # 双引号 C="$A+10" echo $C # 单引号 D=‘$A+10’ echo $D E=`ls` echo $E F=$(ls) echo ${F} # 同$F
-
位置参数
位置参数是一种在调用shell程序的命令中按照各自的位置决定的变量,是在shell脚本之后输入的参数。位置参数之间使用空格进行分割,shell取第一个位置参数的值赋给shell脚本中的$1,第二个位置参数的值赋给$2,以此类推。$0是一个特殊变量,其内容为当前shell脚本的文件名,所以$0不是一个位置参数。
#! /bin/bash # $# 是一个预定义变量,表示位置参数的数量 echo The para count is: $# echo The bash script name is: $0 echo The first pos para is: $1 echo The second pos para is: $2
-
预定义变量
预定义变量是在shell脚本一开始就定义的变量,shell脚本预定义了若干个变量供用户使用,所有的预定义变量都由$和另一个字符构成。常见的shell预定义变量如下所是:
预定义变量 含义 $# 位置参数数量 $* 所有位置参数的内容 $? 命令执行后的返回状态 0:正常 1:错误 $$ 当前进程的进程号 $! 后台运行的最后一个进程号 $0 当前执行的进程名 (shell程序的文件名) $@ 传递给脚本或者函数的所有参数内容 示例代码
#! /bin/bash echo "Current file name: $0" echo "First Para is : $1" echo "Second Para is : $2" echo "Inputed Para1: $@" echo "Inputed Para2: $*" echo "Total Parameter number: $#" echo "Current process id is: $$" exit 1
Note
$* 和 $@ 都表示传递给函数或者脚本的所有参数,不被双引号包含时,都以"$1","$2","$3"...的形式输出所有参数。当被双引号包围时,"$*"会将所有的参数作为一个整体输出,以"$1 $2 $3 ... $n"的形式进行输出。"$@"会将各个参数分开,以"$1" "$2" "$3" ... "$n"的形式输出所有参数。示例代码
#! /bin/bash # 无双引号 echo "\$*="$* # 有双引号 echo "\"\$*\"=""$*" # 无双引号 echo "\$@="$@ # 有双引号 echo "\"\$@\"=""$@" # 循环输出 echo "Print each param in \$@" for var in $@ do echo var is: $var done echo "Print each param in \"\$@\"" for var in "$@" do echo var is: $var done echo "Print each param in \$*" for var in $* do echo var is: $var done echo "Print each param in \"\$*\"" for var in "$*" do echo var is: $var done
条件判断
测试表达式
在shell的各种条件结构和流程控制结构中都要进行各种测试,然后根据测试结果的不同执行不同的分支,通常会与if等条件语句相结合,来完成测试判断,减少程序执行错误。
几种条件测试语法
- test 测试表达式
- [ 测试表达式 ]
- [ [ 测试表达式 ] ]
- ((测试表达式))
条件测试语法比较
test | [ ] | [ [ ] ] | (()) | |
---|---|---|---|---|
边界是否需要空格 | 需要 | 需要 | 需要 | 不需要 |
逻辑操作符 | ! -a -o | ! -a -o | ! && || | ! && || |
整数比较操作 | -eq -gt -lt -ge -le | -eq -gt -lt -ge -le | -eq -gt -lt -ge -le 或 = > >= < <= |
-eq -gt -lt -ge -le 或 = > >= < <= |
字符串比较操作 | = == != | = == != | = == != | 不支持 |
文件操作 | -d -f -e -s -r -x -w L -nt -ot |
-d -f -e -s -r -x -w L -nt -ot |
-d -f -e -s -r -x -w L -nt -ot |
不支持 |
是否支持通配符匹配 | 不支持 | 不支持 | 不支持 | 不支持 |
文件测试操作符
文件测试操作符 | 说明 |
---|---|
-d | 文件存在且类型为目录,返回真 |
-f | 文件存在且类型为文件,返回真 |
-e | 文件存在返回真 |
-s | 文件存在且大小不为0,返回真 |
-r | 文件存在且可读,返回真 |
-w | 文件存在且可写返回真 |
-x | 文件存在且可执行,返回真 |
-L | 文件存在且为链接文件,返回真 |
f1 -nt f2 | 文件f1比文件f2新,返回真 |
f1 -ot f2 | 文件f1比文件f2旧,返回真 |
test 示例代码
#! /bin/bash
echo "Test the file myfile.txt.\n"
if test -e ./myfile.txt ; then # 文件存在
echo "File \"myfile.txt\" exist.\n"
if test -d ./myfile.txt ; then # 是否为目录
echo "File \"myfile.txt\" is a directory.\n"
else
echo "File \"myfile.txt\" is file.\n"
if test -r ./myfile.txt -a -w ./myfile.txt ; then # 可读 且 可写
echo "File \"myfile.txt\" can read and write.\n"
elif test -x ./myfile.txt ; then # 可执行
echo "File \"myfile.txt\" can execute.\n"
else
echo "File \"myfile.txt\" can not both read and write and execute.\n"
fi
fi
else
echo "File \"myfile.txt\" not exist.\n"
fi
输出:
[ ]示例代码
#! /bin/bash
echo "Test the file myfile.txt.\n"
if [ -e ./myfile.txt ] ; then # 文件存在
echo "File \"myfile.txt\" exist.\n"
if [ -d ./myfile.txt ] ; then # 是否为目录
echo "File \"myfile.txt\" is a directory.\n"
else
echo "File \"myfile.txt\" is file.\n"
if [ -r ./myfile.txt ] && [ -w ./myfile.txt ] ; then # 可读 且 可写
echo "File \"myfile.txt\" can read and write.\n"
elif [ -x ./myfile.txt ] ; then # 可执行
echo "File \"myfile.txt\" can execute.\n"
else
echo "File \"myfile.txt\" can not both read and write and execute.\n"
fi
fi
else
echo "File \"myfile.txt\" not exist.\n"
fi
字符串测试操作符
常用字符串测试操作符 | 说明 |
---|---|
-n | 若字符串长度不为0,返回真 |
-z | 若字符串为空,返回真 |
str1 == str2 | 字符串相等 |
str1 != str2 | 字符串不相等 |
note : == 和 !=两端都要有空格
#! /bin/bash
str1="hello"
str2="world"
# test 判断
if test -z $str1 -o -z $str2 ; then
echo "str1 or st2 is empty.\n"
else
if test $str1 == $str2 ; then
echo "str1 equals to str2.\n"
else
echo "str1 not equals to str2.\n"
fi
fi
# [] 判断
if [ -z $str1 ] || [ -z $str2 ] ; then
echo "str1 or st2 is empty.\n"
else
if [ $str1 == $str2 ] ; then
echo "str1 equals to str2.\n"
else
echo "str1 not equals to str2.\n"
fi
fi
比较运算符 (整数比较)
表示方法1 | 表示方法2 |
---|---|
-gt | > |
-ge | >= |
-lt | < |
-le | <= |
-eq | == |
#! /bin/bash
var1=3
var2=4
if test 3 >= 4 ; then
echo "var1 equals to var2.\n"
else
echo "var1 not equals to var2"
fi
逻辑运算
表示方法1 | 表示方法2 | 说明 |
---|---|---|
-a | && | 与 |
-o | || | 或 |
! | ! | 非 |
case条件判断语句
case条件语句相当于if多分支语句,但是其格式更加的工整规范,适合实现系统服务启动脚本应用
```bash
#! /bin/bash
read -n1 -p "Are you sure to start the routine(y or n) ?" choice
echo # 换行
case $choice in
[yY]) # y or Y
echo "Starting the routine." # 会自动换行
;;
"n" | "N") # n or N
echo "Exiting the routine."
;;
*) # 相当于default
echo "Unexpected input."
esac
read -n1 -p "Please input content(char): " content
echo
case $content in
[a-z]) # 范围a-z
echo "Lower case letter."
;;
[A-Z])
echo "Uper case letter."
;;
[0-9])
echo "number."
;;
*)
echo "Unknown content"
esac
```
循环
数组
在shell脚本中,采用括号来定义数组,数组元素使用空格隔开
#! /bin/bash
# 数组定义
array=(1 2 3 4 5)
array1[0]=1
array1[1]=1
array1[2]=1
array1[3]=1
# 读取数组元素
ele=${array[0]}
echo -e "First element in array is $ele" # 添加 -e可实现换行 \c表示不换行
# 获取数组长度
len=${#array[@]} # 等效于 ${#array[*]}
# 获取特定元素的长度
eleLen=${#array[0]}
echo Array length is: $len
echo Element len is: $eleLen
# 数组遍历 (不带下标)
for var in ${array[@]} ; do
echo Value: $var
done
array2=("Hello" "world" "test" "thus")
# 数组遍历 (带下标)
for((i=0; i<${#array2[@]};i++)); do
echo Value is: ${array2[i]}
if test $i -eq 1 ; then
echo "Element length is ${#array2[i]}"
fi
done
for语法格式
for 条件
do
命令
done
# 或者
for 条件 ; do
命令
done
while循环
while 条件
do
命令
done
循环控制语句
命令 | 说明 |
---|---|
break n | n表示跳出循环的层数,如果省略,则表示跳出整个循环 |
continue n | n表示退到第n层循环继续,如果省略,则表示跳过本次循环 |
exit n | 推出当前shell程序,n为上一次执行的返回值,在下一个shell里面可以通过$?接收n的指 |
示例代码
#! /bin/bash
# 单层break
while true ; do
read -n1 -p "Please input a number: " number
echo
case $number in
[0-9])
echo You input a number: $number
;;
*)
echo It is not a number.
break # 也可修改为continue
;; # note 可写可不写
esac
done
# 多层break
for var1 in 1 2 3 ; do
for var2 in 7 8 9 ; do
if [ $var1 -eq 3 -a $var2 -eq 9 ] ; then
break 2 # 跳出两层循环
else
echo -e "Result: $var1 + $var2 \n"
fi
done
done
函数
与其他编程语言一样,shell脚本也支持函数,函数必须线定义再使用
函数的定义格式如下所示:
func_name () {
command1
command2
...
[return value]
}
函数的返回值为return语句返回的指,如果没有return语句,则会将最后一条命令执行的结果作为返回值。且shell函数的返回值只能是整数,一般用来表示执行成功与否,0表示成功,其他值表示失败。如果return了其他的数据类型,比如字符串,则会出现错误提示:"numeric argument required".
如果需要返回字符串,可以在函数外预先定义一个字符串变量,在函数中用来接收函数的执行结果,在函数执行完成之后,再去访问预先定义的变量,获取函数的执行结果
#! /bin/bash
# define function
hello () {
echo -e "Hellow everyone."
}
# invoke function
hello
带有返回值的函数
#! /bin/bash
# 定义函数
funcSum () {
read -p "Please input one number: " num1
read -p "Please input another number: " num2
return $(($num1 + $num2))
}
# 调用函数
funcSum
# 获取函数调用返回值
ret=$?
echo Sum is: $ret
函数传参
在shell脚本调用函数的时候,可以向其传递参数,在函数内部可通过$n的指来获取参数的值,注意当n>=10的时候,需要通过${n}来获取参数的值.
#! /bin/bash
funcPara () {
# 参数
echo "Para 0 is: $0" # 脚本文件的文件名
echo "Para 1 is: $1"
echo "Para 2 is: $2"
echo "Para 3 is: $3"
# 获取所有参数
echo "All para are: $*"
}
funcPara 1 2 3 4
note
unset除了可以删除变量,也可以删除函数,删除函数的时候需要加上.f选项(unset具体使用见后续Linux命令):
$unset .f funcName
此外,如果需要直接从终端调用函数,可以将函数定义在主目录下的.profile文件中,每次登陆后,在命令提示父后面可直接调用函数
输入输出重定向
Unix命令默认从标准输入(stdin)设备获取输入,将结果输出到标准输(stdout)出设备。
输出重定向
命令的输出不仅可以是标准输出,还可以将其转移向文件,这被称为输出重定向,输出重定向的语法为
command > filename
输出重定向会覆盖文件原有的内容,如果希望文件内容不被覆盖,可按照如下方法将输出内容追加到文件末尾
command >> filename
command >> filename
...
输入重定向
类似于输出重定向,Unix命令也可以从文件获取输入,语法为
command < file
重定向之后,本来需要从键盘获取输入的命令会转移到文件读取内容,例如统计某文件的行数:
wc -l < filename
补充内容
一般情况下,每个Linux命令运行,都会打开以下三个文件:
- 标准输入文件(stdin) stdin文件的文件描述符为0,Linux命令默认从stdin读取数据
- 标准输出文件(stdout) stdout文件的文件描述符为1,Linux命令默认向stdout输出数据
- 标准错误文件(stderr) stderr文件的文件描述符为2,Linux命令默认向stderr写入错误信息
如果希望将stderr重定义到文件,可采用如下的方法:
command 2>file
command 2>>file # 追加
如果希望将标准输出和标准错误输出合并到同一个文件,可采用如下的方法
# 文件描述符1和文件描述符2合并,重定向到文件输出
command > file 2>&1
command >> file 2>&1
# 读如也可以采取合并
# n <& m 将输入文件m和n进行合并
/dev/null文件
在执行某个命令的时候,如果希望不再屏幕上显示输出结果,可以将输出结果重定向到/dev/null,这是一个特殊的文件,写入这个文件的内容都会被丢弃,如果从该文件读取内容,将读取不到任何内容。可以将输出重定向到这个文件,起到禁止命令执行结果输出的效果。
command>/dev/null 2>&1
xargs参数过滤
xargs (extended arguments)是给命令传递参数的一个过滤器,也是组合多个命令的一个工具,
Linux常用命令
read命令
Linux read命令用于从标准输入读取数值,当使用重定向的时候,可以读取文件中的一行数据
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
参数说明
- -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符
- -d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志(作为什么结束的标志?)
- -p 后面跟提示信息,即在输入前打印提示信息
- -e 在输入的时候可以使用命令补全功能
- -n 后跟一个数字,定义输入文本的长度,当输入计数到定义的长度后,读取自动停止
- -r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了
- -s 安静模式,在输入字符时不再屏幕上显示,例如login时输入密码
- -t 后面跟秒数,定义输入字符的等待时间
- -u 后面跟fd,从文件描述符中读入,该文件描述符可以是exec新开启的
示例代码
#! /bin/bash
# -p参数
read -p "Please input option: " Opt
echo The user input is: $Opt
# -d参数
read -de cm # 将输入读取到变量cm中,碰到字符e则停止读取(读取结果不包含e),无需按回车
echo # 换行
echo The user input is: $cm
# -n参数
read -n5 -p "Please input your name: " name # 限定长度为5,读取计数到5自动结束
echo # 换行 自动结束的需要添加换行
echo The user name is: $name
# -s参数
read -s -p "Please input code: " code
echo The code is: $code
# -t参数
if read -t3 -p "Please input content: " content ; then
echo The content is : $content
else
echo You has expired!
fi
echo命令
echo命令用于字符串输出
echo content # 会默认换行
参数:
- -e 开启转义,打印出转义字符
- -n 去掉echo末尾默认的换行符
示例代码
echo This is a test str
echo "This is a test str"
echo \"This is a test str\"
echo "\"This is a test str\""
echo "This is a test \n"
#换行 -e开启转义,实现换行 \n
echo -e "This is a test \n"
# 不换行 -e开启转义,实现不换行 \c
echo -e "This is a test \c"
echo "Continue"
# 显示执行结果
echo `date`
# 显示结果定向至文件
echo "This is a test.\n" > myfile.txt
file命令
file命令主要用于辨识文件的类型
file [-bcLvz][-f <名称文件>][-m <魔法数字文件>...][文件或目录...]
参数含义
- -b 列出辨识结果时,不显示文件名
- -c 详细显示指令的执行过程,便于分析程序执行情形
- -f 指定文件名称,当有一个或者多个文件名称的时候,让file依次辨识这些文件
- -L 显示链接符号所指向的文件的类型
- -m 制定魔法数字文件
- -v 显示版本信息
- -z 尝试去解读压缩文件的内容
- [文件或目录列表] 要确定类型的文件列表,多个文件之间使用空格分隔开来,可使用shell通配符来匹配多个文件
示例代码
#! /bin/bash
# 获取当前目录
cur_dir=$(pwd)
script_path=$cur_dir/demonscript4.sh
file $script_path
file -b $script_path
file -i $script_path
file -i -b $script_path
不同文件类型的魔术数字(附加)
文件类型的魔术数字,指定是文件的最开头的几个用于唯一区别其它文件类型的字节,有了这些魔术数字,我们就可以很方便的区别不同的文件,这是除了后缀名区分文件类型的另一种区分文件类型的方法。例如,对于一个JPEG文件,开头的可能是“ffd8 ffe0 0010 4a46 4946 0001 0101 0047 ……JFIF…..G”之类的字符,"ffd8"就表示这个文件是一个JPEG类型的文件,"ffe0"表示这个文件是一个JFIF结构,以下列出几种常见文件类型的魔术数字以及对应的ASCII字符:
文件类型 | 后缀名 | 魔术数字 | ASCII字符 |
---|---|---|---|
Bitmap Format | .bmp | 42 4d | BM |
GIF Format | .gif | 47 49 46 38 | GIF8 |
JPEG File Interchange Format | .jpg | ff d8 ff e0 | .... |
PNG format | .png | 89 50 4e 47 | .PNG |
pkzip format | .zip | 50 4b 03 04 | PK.. |
获取当前shell脚本所在的路径
介绍两种shell脚本中获取脚本文件所在路径的方法
#! /bin/bash
# 方法1 获取当前目录
cur_dir=$(pwd)
# 方法2 获取当前目录
cur_dir=`cd $(dirname $0); pwd`
# dirname $0,取得当前执行的脚本文件的父目录 pwd 显示当前工作目录
cut 命令
cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段写至标准输出,如果没有指定file参数,cut命令将读取标准输入,显示每行从头开始算起num1到num2的文字,语法如下所示:
cut [-bn] [file]
cut [-c] [file]
cut [-df] [file]
参数含义
- -b 以字节为单位进行分割,这些字节位置将忽略多字节边界符,除非指定了-n参数
- -c 以字符为单位进行分割
- -d 自定义分割符,默认为制表符
- -f 提取指定的字段
- -n 取消分割多字节字符,仅仅和-b一起配合使用
unset命令
unset命令用于删除已定义的变量(shell变量或者环境变量),或者shell函数
unset [-fv][变量或函数名称]
参数含义
- -f 仅删除函数
- -v 仅删除变量
export usernametest=ubantu_all # 定义环境变量
echo $usernamett # 查看环境变量
unset usernamett # 删除环境变量
zip命令
zip压缩文件
.zip格式是windows和Linux通用的压缩文件格式,命令的基本格式如下
zip [-AcdDfFghjJKlLmoqrSTuvVwXyz$][-b <工作目录>][-ll][-n <字尾字符串>][-t <日期时间>][-<压缩效率>][压缩文件][文件...][-i <范本样式>][-x <范本样式>]
参数含义(常用几个参数)
- -r 递归压缩目录
- -m 压缩完成后,删除原始的文件
- -v 显示压缩的详细过程
- -q 压缩的时候不显示命令的执行过程
- -n n取(1-9),压缩级别,1代表压缩速度更快,9代表压缩效果更好
- -u 更新压缩文件,即往压缩文件中添加新文件
示例代码
#! /bin/bash
# 获取当前脚本路径
cur_dir=`cd $(dirname $0); pwd`
echo "Current script path is $cur_dir"
filenames=test # 文件名
filecount=4 # 文件个数
fileSuffix=.txt # 文件后缀名
# 数组
files=()
for((i=0;i<$filecount;i++)); do
index=$(($i+1))
#let index=$i+1 # i+1等效写法
#index=`expr $i + 1` # i+1等效写法
filenamepath=${cur_dir}/${filenames}${index}${fileSuffix}
if [ -e $filenamepath -a -f $filenamepath ]; then
rm -f $filenamepath
if [ $? -eq 0 ]; then
echo "Success delete file \"$filenamepath\"" # 删除成功,给出提示
fi
fi
touch $filenamepath
if test $? -eq 0 ; then # 获取touch命令的执行结果,0 成功
echo "Success created file \"$filenamepath\"" >> $filenamepath
fi
# 文件名保存在数组中
files[$i]=$filenamepath
done
# 压缩文件的个数 求取数组长度
filescount=${#files[@]}
echo "Total $filescount file(s) to be compressed."
for((i=0;i<$filescount;i++)); do
filename=${files[i]}
#let fileindex=$i+1
#if [ $fileindex -ge 3 ] ; then
# break
#fi
echo "zipping file \"$filename\""
if test $i -eq 0 ; then
zip -2 test.zip $filename
continue
fi
zip -u -2 test.zip ${filename} # 所有文件压缩
done
echo "------End-----"
unzip命令解压文件
unzip命令用于解压.zip压缩文件
unzip [-cflptuvz][-agCjLMnoqsVX][-P <密码>][.zip文件][文件][-d <目录>][-x <文件>] 或 unzip [-Z]
参数含义
- -d 将压缩文件解压到指定目录下 unzip -d dirname *.zip
- -n 解压时不覆盖已存在的文件
- -o 解压时覆盖已存在的文件,且无需用户确认
- -v 查看压缩文件的详细信息,包括压缩文件中包含的文件大小、文件名以及压缩比等,但并不做解压操作
- -t 测试压缩文件是否损坏,但是不解压文件
- -x 解压文件,但是-x指定的文件会跳过,不进行解压
示例代码
#! /bin/bash
unzip -d ./unzipdir ./test.zip # 指定解压目录
unzip ./test.zip -x file1 file2 # 不解压file1 file2
cp命令
cp命令用于将一个或者多个文件,或者目录复制到指定位置,也可用于文件的备份
参数含义
- -f 若目标文件已经存在,则会直接将目标文件进行覆盖
- -i 若目标文件已经存在,则会询问是否进行覆盖
- -p 保留源文件或者源目录的所有属性
- -r 递归复制,用于文件夹拷贝
- -b 覆盖已存在的目标文件时,将目标文件进行备份
- -v 详细显示cp命令执行的操作过程
- -d 当复制符号链接的时候,把目标文件或者目录也建立为符号链接,并将其指向源文件或者元目录所连接的文件或者目录
- -l 对源文件建立硬连接,而非复制文件
- -s 对源文件建立软连接,而非复制文件
- -a 等价于-pdr
find命令
find命令是Linux中强大的搜索命令,可以按照文件名,文件权限,文件大小,时间,inode号等信息搜索文件,因为find命令是直接在硬盘中进行搜索,因此对其指定合适的搜索范围特别重要,如果搜索的范围过大,就会导致消耗较大的系统资源。
命令格式:
find path -option [ -print ] [ -exec -ok command ] {} \;
path : 搜索路径
option : 搜索选项
选项
- -name 按照文件名进行搜索
- -iname 按照文件名进行搜索,不区分大小写
- -inum 按照inode号进行搜索
- -size[+-] 按照文件大小进行搜索, +表示大于size的文件,-表示小于size的文件,(M, k)
- -type 按照特定的类型查找文件, d-目录 f-一般文件 l-符号链接 s-套接字文件 c-字符设备 b-块设备
- -print 将搜索结果输出到标准输出
- -ctime 按照文件创建时间进行搜索 [+-n]
- -mtime 按照文件更改时间进行搜索 [+-n]
- -atime 按照文件访问时间进行搜索 [+-n]
- -perm 按照文件权限进行搜索
按照文件类型搜索,可选的文件类型如下所示
符号 | 含义 |
---|---|
d | 搜索目录 |
f | 搜索一般文件 |
l | 搜索链接文件 |
s | 搜索套接字文件 |
c | 搜索字符设备文件 |
b | 搜索块设备文件 |
按照文件大小搜索,表示文件大小的可选单位如下所示:
符号 | 含义 |
---|---|
b | 块 512字节 |
c | 字节 |
w | 字 |
k | 千字节 |
M | 兆字节 |
G | 吉字节 |
按照修改时间,创建时间,访问时间进行修改,指定时间时可通过+-来指定特定范围的时间,具体使用方式如下所示:
符号 | 含义 |
---|---|
-n | 表示从当前时间算起,n天以内 |
n | 表示从当前时间算起,第n年 |
+n | 表示从当前时间算起,第n天以前 |
示例代码
find ./ -name "*.txt" # 查找后缀名为.txt的文件
find ./ -type f # 查找普通文件
Linux
Linux系统目录结构
在Linux或者Unix系统中,所有文件和目录都被组织成以一个根节点开始的树状结构,文件系统的最顶层是由根目录开始\
,根目录下既可以是文件。如果一个目录或者文件是以.
开头的,则表示这个文件或目录是隐藏的。系统目录结构如下所示:
目录具体含义:
- /bin Binaries(二进制文件)的缩写,存放着最经常使用的命令
- /boot 用于存放启动Linux时所需要的一些核心文件,包括一些链接文件以及镜像文件
- /dev Devices的缩写,用于存放Linux的外部设备,在Linux中设备也是以文件的形式进行表示的,访问设备的方式与访问文件的方式类似。
- /etc Etcetera(等等)的缩写,用来存放所有系统管理所需要的配置文件和子目录
- /home 用户的主目录,在Linux中,每个用户都有一个自己的目录,一般是以用户的账号进行命名的,如上途中的alice,bob等
- /lib Library(库)的缩写,这个目录存放着系统最基本的动态链接共享库,几乎所有的应用程序都会用到这些共享库
- /lost+found 这个目录一般是空的,当系统非法关闭后,会暂时保存一些文件
- /media Linux系统会自动识别一些设备(u盘,光驱),识别到后会将这些设备自动挂载此目录下
- /mnt 系统提供该目录是为了让用户临时挂载别的文件系统,例如光驱或者u盘
- /opt 存放其他需要额外安装的软件
- /proc Process(进程)的缩写,/proc是一种伪文件系统(即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟目录,可以通过直接访问这个目录来获取系统信息。这个目录的内容不在硬盘上,而是在内存里面。可以通过直接修改里面的某些文件,来实现对系统的设置,例如,可以向特定的文件写入内容,来实现屏蔽主机的ping命令:
echo 1 /proc/sys/net/ipv4/icmp_echo_ignore_all
,向这个文件写入1,就可实现屏蔽ping命令,使得其他主机无法ping到当前主机。 - /root 为系统管理员的用户目录
- /selinux 这个目录是 Redhat/CentOS 所特有的目录,Selinux 是一个安全机制,类似于 windows 的防火墙,这个目录存放selinux相关的文件。
- /sys 这是Linux2.6内核的一个重大变化,该目录下安装了2.6内核中新出现的一个文件系统sysfs,sysfs文件系统集成了以下三种文件系统的信息:
- 针对进程信息的proc文件系统
- 针对设备的devfs文件系统
- 针对伪终端的devpts文件系统
- /tmp Temporary的所缩写,用来存放一些临时文件
- /usr Unix Shared Resources, 共享资源目录,用户的很多应用程序和文件都存放在这个目录下,相当于Windows的Program Files目录
- /usr/bin 系统用户使用的应用程序
- /usr/src 内核源代码默认的放置目录
- /var Variable缩写,这个目录存放着不断扩充的东西,通常会将经常被修改的目录或者文件存在这里,例如日至文件
- /run 是一个临时文件系统,存储系统启动以来的信息,当系统重启的时候,这个目录下的文件应该被删掉或清除。
软链接与硬链接
Linux文件系统中,数据分为元数据和用户数据,用户数据,即文件数据块(data block)是记录文件真实内容的地方。元数据是文件的附加属性,记录了如文件大小,创建时间,属主,inode(索引节点,为一个结构体)等信息。在Linux文件系统中,文件名并不是文件的唯一标识,元数据中的inode号才是文件的唯一标识。为解决文件的共享使用问题,Linux引入了应链接和软链接的概念,通过链接,隐藏了源文件的路径,提升了权限安全以及节省了内存。
文件名只是为了方便区分和记忆,在Linux文件系统中,系统和程序是通过inode号来寻找正确的文件数据块,通过文件名获取文件内容的过程如下:
filename-->inode-->data block
在Linux中,可通过ls -i filename
来查看文件的inode号,可以通过stat filename
查看文件的inode信息
硬链接
若一个inode号对应多个文件名,则城这些文件为硬链接,硬链接是指一个文件使用了多个别名,但是这些文件都具有相同的inode号。
特点:
- 所有文件具有相同的inode号以及data block
- 只能对已经存在的文件创建硬链接
- 不能对目录创建硬链接
- 不能交叉文件系统创建硬链接(因为inode号只在各文件系统下是唯一的,当Linux系统下挂载了多个文件系统后,可能出现inode号重复)
- 删除一个硬链接并不影响其他的硬链接
可以使用命令df -i --print-type
来查看当前系统中挂载的文件系统类型,各文件系统使用情况以及文件系统挂载点等信息。
软链接
若文件的用户数据快中存放的是另一个文件的路径指向,则该文件就称为软链接,因此软链接本质上就是一个文件,只是文件的内容比较特殊,软链接有自己的inode号以及用户数据,因此软链接的创建和锁定和使用限制较少。
特点:
- 软链接有自己的文件权限及属性
- 可对不存在的文件或者目录创建软链接
- 软链接可交叉文件系统进行创建
- 删除软链接并不影响被指向的文件。若被指向的文件被删除,则相关的软链接称为死链接(dangling link),若被指向的文件被重建,则死链接可恢复为正常的链接
链接的创建
ln -s filename softlink # 建立软链接
ln filename hadrlink # 建立硬链接
cp -s filename softlink # 建立软链接
cp -l filename hardlink # 建立硬链接
文件存储-关于inode补充
文件存储在硬盘上,硬盘的最小存储单位叫做扇区(sector),每个扇区存储512个字节,操作系统在读取硬盘的时候,不会一个扇区一个扇区的读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个块(Block),这种由多个扇区组成的块,是文件存取的最小单位,块的大小,最常见的是4kB,即8个连续的扇区组成一个快。文件数据都存储在块中,那么,还必须找到一个地方存储文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。
inode包含文件的元信息,具体来说有如下内容:
- 文件的字节数
- 文件拥有者的user id
- 文件的group id
- 文件的 r w x 权限
- 文件的时间戳,ctime表示inode上一次变动的时间,mtime表示文件内容上一次的变动时间,atime表示文件上一次打开的时间
- 文件数据Block的位置
inode也会占用硬盘的空间,因此在硬盘格式化的时候,操作系统会自动将硬盘分为两个区域,一个是数据区,用于存放所有的文件数据;另一个是inode区(inode table),用于存放inode所包含的信息。每个inode的大小为128或者256个字节,inode节点的总数,在格式化的时候就已经给定,一般是1kB或者2kB就设置一个inode。假定在一块1G的硬盘中,每个inode节点的大小为128字节,每1KB设置一个inode,那么inode table的大小就会达到128M,占据整块硬盘容量的12.8%,可以使用df -i
查看每个硬盘下的inode总数以及使用情况(可以看到每个硬盘下的inode总数,以使用的inode数量,未使用的inode数量等信息)
每个文家都必须有一个inode,因此可能会发生inode已经用光,但是硬盘还未满的情况,此时也是无法进行文件创建的
目录文件:
在Linux系统中,目录也是一种文件,打开目录,实际上就是打开目录文件,目录文件的结构较为简单,其内容是由一些列目录项构成的目录列表,每个目录项由两部分构成,该目录下文件的文件名,以及文件名所对应的inode号。由ls -i dir
可查看特定目录下的所有目录项。
目录文件的链接数:
创建文件的时候,默认生成两个目录项.
和..
,.
相当于当前目录文件的硬链接,而..
相当于当前目录父目录的硬链接,因此,任何一个目录的硬链接总数,等于2加上它的子目录总数,这里的2是父目录对其的“硬链接”和当前目录下.
对其的硬链接
Linux文件类型
Linux常见的文件类型有七种:
表示符号 | 文件类型 | 补充 |
---|---|---|
- | 常规文件 | 即file |
d | 目录文件 | 目录 |
b | 块设备文件 | Block Device 文件,如硬盘,支持以Block为单位进行随机访问 |
c | 字符设备文件 | Character Device 文件,如键盘支持以字符为单位进行线性访问 |
l | 符号链接文件 | Symbol Link 软链接文件 |
p | 管道文件 | pipe文件 |
s | 套接字文件 | socket文件 |
对于普通文件,ls -l
会显示文件的大小,但是在设备类文件中,会显示两个用逗号隔开的数字,这两个数字不表示文件的大小。第一个数字表示主设备号,第二个数字表示次设备号,主设备号用于区分设备类型,用于决定加载设备的驱动程序。次设备号用于区分同一类型的不同设备,如硬盘1,硬盘2
文件基本属性
Email: [email protected] 测试
web: www.baidu.com
ceshi
标签:脚本,文件,链接,shell,笔记,echo,Linux,inode,目录 From: https://www.cnblogs.com/ncepubye/p/17013021.html