第一章 小试牛刀
echo 后面可以直接跟内容,或者用单引号和双引号
1.单引号中的变量不会被解析,比如echo '$aa' 结构就是 $aa
2.不带引号时,echo hello;hello 的第二个hello会被当做命令执行
- echo -n 忽略换行
- echo -e 输出的内容会被转移
printf
- %-5s 打印字符串,- 表示左对齐,不到5位的用空格替代
- $s格式化字符串,%c是替换字符,%d是数字,%f是浮点数
输出带色彩的文件和背景
- echo -e "\e[1;45m \e[1;31m sssssssssssssss \e[0m"
变量的一些简单实用
- var=hello
- 输出var变量
- echo $var
- echo ${var}
- 求字符串长度
- echo ${#var}
- 当前的shell版本
- echo $SHELL
- echo $0
数学运算
- let var++ #执行let时候变量前面不需要加$符号
- echo $var
- result=$[no1 + no2] #[]中也可以使用$前缀
- result=$[$no1 + 5]
- result=$(( no1 + 5 )) #()这种写法时,变量前必须加上$
- echo "4*3.14" | bc #只有bc才支持浮点运算
调用一个shell有两种方式,调用一个shell执行然后得到它的返回值
- result=`expr 3+4`
- result=$(exprt $no1+5)
输入输出
- 0 stdin(标准输入)
- 1 stdout(标志输出)
- 2 stderr(标志错误)
- > 等同于1>; >>等同于1>>
- echo "hello " 2>&1 out.log
- echo "hello" &> out.log
自定义文件描述符(注意 > 符号和数字之间不能有空格)
- echo "this is new line" > input.txt
- exec 3
- cat <&3
- exec 4 > out.txt
- echo "new line" >& 4
- exec 5>>input.txt
- echo "append" >&5
- echo "aaa" 2> /dev/null #错误输出重定向到黑洞文件
数组
- array_var=(1,2,3,4,5,6)
- array_var[0]="test1"
- array_var[1]="test2"
- array_var[2]="test3"
- echo $(array_var[0])
- echo $(array_var[$index]) #假设index=5
- echo $(array_var[@])
- 结果: test1 test2 test3 test4 test5 test6
- echo $(#array_var[*]) #打印数组长度
关联数组
- declare -A ass_array
- ass_array=(([index1]=val-1 [index2]=val-2))
- 或者:
- ass_array[index1]=val-1
- ass_array[index2]=val-2
- fruits_value=( [apple]='100 dollars' [orage]='150 dollars' )
- 列出数组索引
- echo $(!array_var[*])
- echo $(!array_var[@])
- 语法 描述
- ${!array[*]} 取关联数组所有键
- ${!array[@]} 取关联数组所有键
- ${array[*]} 取关联数组所有值
- ${array[@]} 取关联数组所有值
- ${#array[*]} 关联数组的长度
- ${#array[@]} 关联数组的长度
别名
- alias rm='cp $@ ~/backup; rm $@'
- 可以将其放入到 ~/.bashrc文件中,新的shell进程就会生效,删除可以从这个文件中删除或者使用
- unalias命令
时间
- sleep 秒
- usleep 微妙(百万分之一秒)
- date,格式化字符串
- 星期 %a(简写) %A(完整)
- 月 %b(简写) %B(完整)
- 日 %d
- 年 %y(两位表示) %Y(四位表示)
- 小时 %H
- 分钟 %M
- 秒 %S
- 纳秒 %N
- 时间戳 %s
调试
- sh -x script.sh
- set -x: 在执行时显示参数和命令
- set +x: 禁止调试
- set -v: 当命令进行读取时显示输入
- set +v: 禁止打印输入
- function DEBUG() {
- [ "$_DEBUG" == "on" ] && $@ || :
- }
- 可以将调试功能设置为on来运行脚本
- _DEBUG=ON ./scritp.sh
- DEBUG函数中的 : 告诉shell不要执行任何操作
- 把 #! /bin/bash 改成 #! /bin/bash -xv 这样不用其他任何选项就可以启用调试功能了
函数
- 调用函数方式
- myfun ;
- 传递参数
- myfun arg1 arg2 ;
- 参数可以传递给脚本并通过$0(脚本名) 访问
- $1 第一个参数
- $2 第二个参数
- $n 第n个参数
- $@ 被扩展成 "$1" "$2" "$3" 等
- $* 被扩展成 "$1c$2c$3"等
- $@用的最多,由于$*将所有参数当做单个字符串因此很少被使用
- 导出函数,之后函数的作用域就可以扩展到子进程中
- export -f myfun
- 读取命令返回值(状态)
- cmd;
- echo $?
- 利用()可以生成子shell,子shell中执行的内容不会对父shell产生影响
- pwd;
- (cd /bin; ls)
从控制台读取
- read -n number_of_chars variable_name #从输入中读取n个字符并存入变量variable_name中
- read -s var #用不回显的方式去读密码
- read -p "enter input :" var # 显示提示信息
- read -t timeout var #在特定的时间内读取输入read -t 2 var 两秒内输入
- read -d delim_char var #用定界符结束输入行 read -d ":" var
- #输入hello: var被设置为hello
分隔符
- 读取/etc/passwd,打印出用户默认的shell
- line="root:x:0:0:root:/root:/bin/bash"
- oldIFS=$IFS
- IFS=":"
- count=0
- for item in $line;
- do
- [ $count -eq 0 ] && user=$item;
- [ $count -eq 6 ] && shell=$item;
- let count++
- done;
- IFS=$oldIFS
- echo $user\'s shell is $shell
- 结果: root's shell is /bin/bash
- echo {1..50} #能够生产从1到50的数字列表
- echo {a..z}或者{A..Z},或者使用{a..h}生产部分列表
- for i in {a..z} do action; done;
- for循环
- for var in list;
- do
- command;
- done
- 采用c语言中的for循环格式
- for((i=0;i<10;i++)) {
- commands;
- }
- while循环
- while condition
- do
- command;
- done
- 用true作为循环条件能够参数无限循环
- until循环(它会一直执行循环直到给定的条件为真)
- x=0;
- unitl [ $x -eq 9 ];
- do
- let x++;
- echo $x;
- done
比较
- if condition;then
- commands;
- fi
- if condtion;then
- commands;
- elif condtion;then
- commands;
- else
- commands;
- fi
- 条件通常被放置在封闭的中括号内,一定要注意在[或]与操作数之间有一个空格,如果忘记空格脚本就
- 会报错
- 一些重要的操作符:
- -gt 大于
- -lt 小于
- -ge 大于或等于
- -le 小于或等于
- -eq 等于
- -nq 不等于
- 文件系统相关测试
- [ -f $file_var ] 如果给定的变量包含正常的文件路径或文件名则返回真
- [ -x $var ] 如果给定的变量包含的文件可执行,则返回真
- [ -d $var ] 如果给定的变量包含的是目录,则返回真
- [ -e $var ] 如果给定的变量包含的文件存在,则返回真
- [ -c $var ] 如果给定的变量包含的是一个字符设备文件的路径,则返回真
- [ -b $var ] 如果给定的变量包含的是一个块设备文件的路径,则返回真
- [ -w $var ] 如果给定的变量包含的文件可写,则返回真
- [ -r $var ] 如果给定的变量包含的文件可读,则返回真
- [ -L $var ] 如果给定的变量包含的是一个符号链接,则返回真
- 字符串比较(因为有时候采用单个中括号会产生错误,最好使用双个中括号)
- 注意在 = 前后各有一个空格,如果忘记加空格,那就不是比较关系了,而变成赋值语句了
- [[ $str1 = $str2 ]
- [[ $str1 == $str2 ]
- [[ $str1 != $str2 ]
- [[ $str1 > $str2 ] #str1的字母序比str2大
- [[ $str1 < $str2 ]
- [[ -z $str1 ]] #如果str1包含的是空字符串,则返回真
- [[ -n $str1 ]] #如果str1包含的是空字符串,则返回真
第二章 命令之乐
cat相关
- cat file1 file2 file3 #将多个文本内容拼接在一起
- OUTPUT_FROM_SOME COMMANDS | cat #从表中输入中读取
- echo 'from stdin' | cat - file.txt
- cat -s file #压缩空白行
- cat -T file.py #将制表符显示为 ^|
- cat -n file.txt #显示行号
录制和回放终端会话
- script
- scriptreplay
文件查找
- find base_path #base_path 可以是任何位置
- -print 指明打印出匹配的文件名(路径)
- -print0 指明使用'\0'作为定界符来打印每一个匹配的文件名
- -name 后面的参数可以包含正则表达式,指定了文件名所必须匹配的字符串
- -iname 类似-name,但是忽略大小写,如果有多个匹配条件可以用OR,也可以用!否定参数的含义
- find . \(-name "*.txt" -o -name "*.pdf" \) -print
- find . ! -name "*.txt" -print #打印所有不以.txt结尾的文件
- -regex 支持正则表达式
- -iregix 忽略大小写
- find . -iregex ".*\(\.py\|\.sh\)$" #忽略大小写找到所有py和sh结尾的文件
- -maxdepth 深度搜索,最多搜索多少层
- -mindepth 深度搜索,最少搜索多少层
- find . -maxdepth 1 -type f -print #从性能角度来说,最好先指定深度再确定类型
- -type 搜索文件类型
- 普通文件 f
- 符号链接 l
- 目录 d
- 字符设备 c
- 块设备 b
- 套接字 s
- fifo p
- -atime 用户最近一次访问文件的时间,可以带上 + 和 - 表示大于和小于
- -mtime 文件内存最后一次被修改的时间
- -ctime 文件元数据(metadata,列如权限 或所有权)最后一次改变的时间
- find . -type f -atime -7 print #打印出最近七天内被访问过的所有文件
- find . -type f -atime 7 print #打印出恰好在七天前被访问过的文件
- find . -type f -atime +7 print #大餐饮出访问时间超过七天的所有文件
- -amin 访问时间,以分钟为单位
- -mmin 修改时间,以分钟为单位
- -cmin 变化时间,以分钟为单位
- find . -type f amin +7 -print #打印出访问时间超过7分钟的所有文件
- -newer 指定一个用户比较时间长的参考文件,然后找出比参考文件更新的(更长修改时间)文件
- find . -tpe f -newer file.txt -print
- -size 根据文件大小搜索
- b 块(512字节)
- c 字节
- w 字(2字节)
- k 千字节
- M 兆字节
- G 吉字节
- find . -type f -size +2k #大于2k的文件
- -delete 用来删除find查找到的匹配文件
- find . -type f -name "*.swp" -delete #删除当前目录下所有.swp文件
- -perm 根据文件权限和所有权查找(-user 可以指定用户)
- find . -type f -perm 644 -print #打印出权限位644的文件
- find . -type f -user testuser -print #打印出所有testuser用户的文件
- -prune 跳过指定的目录
- find /data0/source_path \( -name ".git" -prune \) -o \( -type f -print \) #忽略git文件
- -exec 可以与其他命令结合(但是只能执行一个命令)
- find . -type f -user root -exec chown testuser {} \;
- #上面的语句是找到所有root所有者的文件修改为testuser,对于每一个匹配的文件{}会被替换成
- #相应的文件名,如果找到两个文件file1.txt和file2.txt那么会执行
- chown testuser file1.txt
- chown testuser file2.txt
- find . -type f -name "*.c" -exec cat {} \;>all_c_files.txt
- #上面的命令是将所有c程序文件拼接起来写入单个文件all_c_files
- find . -type f -mtime +10 -name "*.txt" -exec cp {} OLD \;
- #上面的命令将10天前的.txt文件复制到OLD目录中
- find . -type f -exec ./commands.sh {} \;
- #虽然-exec 参数无法使用多个命令,但是可以执行一个脚本,在脚本中放入多个命令
- find . -type f -name "*.txt" -exec printf "Text file : %s\n" {} \;
- #-exec能够同printf结合起来生产有用的输出信息
玩转xargs
- xargs命令把从stdin接收到的数据重新格式化,再将其作为参数提供给其他命令
- -n 参数表示将输入的内容转换为指定的行
- cat example.txt | xargs -n 3 #将example.txt的内容转换为3行
- -d作为定界符
- echo "splitXsplitXsplitXsplit" | xargs -d X -n 2
- #结果: split split
- split split
- 假设args.txt内容如下:
- arg1
- arg2
- arg3
- -I选项指定一个替换字符串,这个字符串在xargs扩展时会被替换掉。当-I与xargs结合使用时,
- 对于每一个参数,命令都会被执行一次
- cat args.txt | xargs -I {} ./echo.sh -p {} -x
- 结果为:
- ./echo.sh -p arg1 -x
- ./echo.sh -p arg2 -x
- ./echo.sh -p arg3 -x
用tr进行转换
- tr 输入部分 [需要转换的部分] [转换后的部分]
- echo "hello world hehe 1234 aa" | tr 'a-z' 'A-Z'
- #结果: HELLO WORLD HEHE 1234 AA
- 压缩字符
- echo "gun is not unix " | tr -s ' ' #将连续的字符压缩成单个字符
- 删除
- echo "hello world hehe 1234 aa" | tr -d '0-9'
- #结果: hello world hehe aa
- 补集,比如下面这个列子,将不在 0-9,空格,回车中的字符全部删除
- echo hello 1 char 2 next 4 | tr -d -c '0-9 \n'
- #结果: 1 2 4
校验和核实
- md5sum file #创建一个文件的指纹,并将字符串打印到控制台上
- md5sum file > check.md5
- #生成的内容
- 65466125197978378ec6340989ac50db check.log
- md5sum -c check.md5 #对文件进行校验
- sha1sum file #创建一个文件的指纹,并将字符串打印到控制台上
- sha1sum file > check.sha1
- #生成的内容
- 3acec0d3b6e19f68c4d25de104eabb25d5c982d2 checkfile.log
- sha1sum -c check.sha1 #对文件进行校验
排序
- sort -n 按数字进行排序
- sort -r 逆序排序
- sort -M 按照月份排序,一月,二月,三月这样的顺序
- sort -C 测试一个文件是否已经被排序过
- sort -m sorted1 sorted2 合并两个排序过的文件,而且不需要对合并后的文件再进行排序
- sort -k 指定了排序应该按照哪一个键来排序
- sort -b 忽略文件中的前导空白字符
- sort -d 按照字典排序
- sort -o file.log 将排序结果输出到指定文件中
- sort -u 文件去重
- #用法
- sort -C file
- if [ $? -eq 0 ];then
- echo sorted
- else
- echo unsorted
- fi
单一与重复
- uniq命令可以消除重复的内容,但必须用于排序过的数据,一般和sort命令结合使用
- uniq -u sorted.txt 只显示唯一的行(在输入文本中没有出现重复的行)
- uniq -c sorted.txt 统计各行在文件中出现的次数
- uniq -s 2 sorted.txt 指定可以跳过前N个字符
- uniq -w 2 sorted.txt 指定用于比较的最大字符串
- uniq -z file.txt 为每行最后添加\0值字符
临时文件与随机数
- echo $RANDOM #每次都会返回一个随机数
- temp_file="/tmp/file-$RANDOM"
- tmpe_file="/tmp/var.$$" # $$表示当前进程的id
文件分割
- split -b 10k data.file
- ls
- #结果为: data.file xxa xab xac ... axj xa开头的一共10个文件
- split -d 指定数字为后缀
- split -a 4 指定后缀长度
- split -b 10k data.file -d -a 4
- split [command_args] prefix 分割的文件前缀
根据扩展名切分文件名
- file_jpg="sample.jpg"
- echo ${file_jpg%.*} #输出sample
- echo ${file_jpg#*.} #输出jpg
- ${VAR%.*} 表示删除位于%右侧的通配符(也就是 .* ),通配符从右向左进行匹配 这种是非贪婪形的
- ${VAR%%.*} 从右向左执行贪婪操作匹配
- file=hack.fun.book.txt
- ${file%%.*} 输出为hack
- ${VAR#*.} 是从左向右匹配,删除位于#右侧的通配符(也就是 *. )
- ${VAR##*.} 是贪婪匹配,从左向右删除变量中匹配的结果
- file=hack.fun.book.txt
- ${file##*.} 结果是txt
批量重命名和移动
- count=1;
- for img in *.jpg *.png
- do
- new=image-$count.${img##*.}
- mv "$img" "$new" 2> /dev/null
- if [ $? -eq 0 ];then
- echo "renaming $img to $new"
- let count++
- fi
- done
第三章 以文件之名
生成任意大小的文件
- dd if=/dev/zero of=/junk.data bs=1M count=1
- if是input file
- of是output file
- bs是block size
- count代表需要被复制的块数
- 块的大小定义如下:
- 字节(1B) c
- 字(2B) w
- 块(512B) b
- 1k k
- 1m M
- 1g G
文件的交集与差集
- cat a.log
- apple
- gold
- iron
- orange
- silver
- steel
- cat b.txt
- carrot
- cookies
- gold
- orange
- comm a.log b.log
- 结果:
- apple
- carrot
- cookies
- gold
- iron
- orange
- silver
- steel
- 第一列是只在a.log中出现的
- 第二列是只在b.log中出现的
- 第三列是同时在a.log和b.log中出现的
- comm a.log b.log -1 #删除第一列
- comm a.log b.log -2 #删除第一列
- comm a.log b.log -3 #删除第一列
- comm a.log b.log -1 -2 #删除第一和第二列
查找并删除重复文件
- #! /bin/bash
- ls -lS | awk 'BEGIN{
- getline;getline
- size=$5; lastName=$9
- }
- {
- currentName=$9
- if(size==$5) {
- "md5sum "lastName | getline; csum1=$1;
- "md5sum "currentName | getline; csum2=$1;
- if(csum1==csum2) {
- print lastName
- print currentName
- }
- };
- size=$5
- lastName=currentName
- } ' | sort -u > dup_file
- cat dup_file | xargs -I {} md5sum {} | sort | uniq -w 32 | awk '{ print $2 }' | sort -u > dup_sample_file
- comm dup_file dup_sample_file -2 -3 | tee /dev/stderr | xargs rm
- #首先获得文件列表然后遍历并按大小排序,获取文件大小和文件名,如果文件大小相同再比较两个文件
- #的MD5值。如果MD5值相同那么肯定是重复的文件,把这两个文件名打印出来,最后重定向到dup_file中
- #之后cat这个文件再对每行(也就是每个重复的文件)计算MD5值并排序(只比较前32个字节),最后去重
- #只打印一个重复的文件并重定向到dup_sample_file中
- #最后用comm比较dup_file和dup_sample_file,打印出只在dup_file中出现的文件,并用tee打印到标准
- #错误输出中,最后交给xargs 删除这些文件
- #其中上面的功能只需要一行就可以完成了
- ll | awk 'BEGIN{getline} {print $9}' | xargs -I {} md5sum {} | uniq -w 32 -D | awk 'BEGIN{getline}{print $2}' | xargs -I {} rm {}
- #awk中的getline是读取一行,ll的第一行内容不需要所以只读取然后忽略即可,之后用对每个文件做
- #MD5,然后比较前32个字节,打印出所有重复的文件名,之后再用awk,忽略掉第一行(从第二行开始读)
- #然后交给xargs最后删除这些重复文件
文件权限 所有者和粘滞位
- ls的第一位
- - 普通文件
- d 目录
- c 字符设备
- b 块设备
- l 符号链接
- s 套接字
- p 管道
- 之后是9个权限位rwxrwxrwx,用户,组,其他
- 对于用户来说有一个suid位,-rwS------ setuid权限允许用户以其他拥有者的权限来执行可执行文件
- 对于组用户有一个sgid位,----rwS--- setgid位允许以同该目录拥有者所在组相同 的有效组权限来执行文件
- 目录有一个特殊权限,叫粘滞位。当一个目录设置了粘滞位,只有创建该目录的用户才能删除目录中的文件,即使其他用户和组用户有权限也不行。
- 粘滞位出现在其他用户执行的执行权限位置(x),使用t或者T表示,如果没有设置执行权限,但设置了粘滞位则使用t表示;如果同时设置了执行权限和粘滞位就使用T表示
- chmod a+t directory_name #设置粘滞位
- chmod 777 . -R #递归执行
- chmod +s executeable_file #设置sid位
批量生成空文件 创建不可修改文件
- touch file
- for name in {1..100}.txt
- do
- touch name
- done
- touch -a 只更改改文件的访问时间(access time)
- touch -m 只更改文件内容修改时间(modification time)
- touch -d 为文件指定特定的时间戳
- touch -d "Jan 20 2010"
- chattr -i file #设置不可删除的文件
查找符号链接及指向
- ll | grep "^l" | awk '{print $9 " " $10 " " $11}'
- find -type l | xargs readlink
列举文件类型统计信息
- #遍历一个目录下的文件类型,并打印出这种文件类型出现的次数
- #下面这段代码是书中提供的,但是有问题不知道如何修改
- #! /bin/bash
- if [ $# -ne 1 ];then
- echo $0 bashpath;
- echo
- fi
- path=$1
- declare statArray
- find $path -maxdepth 1 -print | while read line
- do
- ftype=`file -b $line`
- let "statArray[$ftype]"++ ;
- done
- for ftype in "${!statArray[@]}"
- do
- echo $ftype : ${statArray["$ftype"]}
- done
- #上面的功能用一行就看以完成了
- find /root -maxdepth 1 -print | xargs -I {} file -b {} | awk '{sum[$0]++} END{ for(i in sum){print i "\t" sum[i]} }'