cat(concatenate)
读取、显示、拼接文件内容
把来自标准输入的内容和文件内容拼接起来:
echo 'from stdin' | cat - test.sh
cat
命令用-
表示标准输入
If file is a single dash (`-‘) or absent, cat reads from the standard input.
其它常用功能选项:
# 给文件内容添上行号
cat -n test.sh
# 把文件中的连续多个空行压缩成一个
cat -s test.sh
find
基本规则
从文件目录向下遍历,匹配符合条件的,处理
# 列出当前目录下所有文件/文件夹、子文件/文件夹
find .
# 用\0分隔(文件路径里有换行符时有用)
find . -print0
# 通配符
find -name "*.js"
# 忽略大小写
find -iname "*.js"
# 多条件
find . \( -name "e*" -o -name "s*" \)
# 路径匹配
find ../tnode -path "*node*"
# 与-path一样,只是参数为正则表达式
find . -regex ".*/e.*h$"
# 忽略大小写
find . -iregex ".*/e.*h$"
# 否定参数(独立参数,可以配合-name/path/regex等用)
find . ! -iregex ".*/e.*h$"
# 例如排除路径含有node_modules的
find ../tnode ! -regex ".*node_modules.*"
# 指定目录深度,-maxdepth 1表示向下找1级(也就是..的孩子,不找孙子)
find .. -name "*.js" -maxdepth 1
# 也可以指定起始深度,-mindepth 2 -maxdepth 2表示只在..的孙子中找,不找儿子也不找孙子的儿子
find .. -name "*.js" -mindepth 2 -maxdepth 2
# 单独用-mindepth找超过指定深度的文件(找深路径lib)
find .. -regex ".*node_modules*.*.js$" -mindepth 20
按文件类型搜索
# 指定文件/文件夹,-type f表示只输出文件
find ../tnode ! -regex ".*node_modules.*" -type f
P.S.参数顺序会影响搜索效率,比如先检查深度再过滤类型更快
文件类型与type
参数值对应关系:
普通文件:f
符号链:l
目录:d
面向字符的设备文件:c
面向块的设备文件:b
套接字:s
FIFO:p
按时间搜索
每个文件有3种时间戳:
访问时间:-atime
修改时间:-mtime
变化时间:-ctime
参数值为整数,表示天数,可以前缀+
和-
,分别表示大于,小于,例如:
# 找出父级目录中,昨天到现在访问过的文件
find .. -type f -atime -1
也有以分钟为单位的:
# -amin, -mmin, -cmin
find .. -type f -amin $((-1 * 60 * 24))
还可以指定一个文件作为参照,找出更新的(修改时间更近的)文件:
# 找出父级目录中,比~/.bash_profile更新的文件
find .. -type f -newer ~/.bash_profile
按文件大小搜索
# 当前目录下大于1K的文件
find . -type f -size +1k
支持b块, c字节, w字, k, M, G
单位,注意前面小写,后两个大写,在其它命令里一般也是这样,例如split
其它用法
# 查找并删除
find . -type f -name "*.tmp" -delete
# 匹配文件权限
find . -type f -perm 777 -print
find . -type f -user ayqy
与-exec
结合执行其它命令
# 查找并格式化输出
find . -type f -exec printf "file: %s\n" {} \;
# 查找并备份
find . -type f -mtime +7 -exec cp {} bak/ \;
P.S.末尾转义分号用来表示-exec
参数值结束,必须要有
-exec
只能执行一条命令,需要执行多条的话,把命令写入文件再执行,例如把备份命令写入bak.sh
:
#!/bin/bash
BAK_DIR=bak
if ! test -e "$BAK_DIR";
then
mkdir "$BAK_DIR"
fi
for file in "$@";
do
cp $file "$BAK_DIR"
done
再查找执行:
find . -type f -mtime +7 -exec ./bak.sh {} \;
-prune
排除不需要查找的东西:
# 跳过.git和node_moudles目录
find . \( -name ".git" -prune \) -o \( -name "node_modules" -prune \) -o \( -type f -print \)
xargs
xargs
命令把从stdin
接到的数据重新格式化,作为参数提供给其它命令,紧跟在管道操作符之后,基本形式:
cmd | xargs
把多行输入转换成单行输出:
# 把换行符换成空格
cat test.sh | xargs
把单行输入转换成多行输出:
# 按每行参数数量断开
echo "1 22 3 4 5 6 7" | xargs -n 3
-d
指定分隔符,实现字符串split
:
# split
echo "1,2,3,4" | xargs -d ,
# `-d`参数是GUN扩展,FreeBSD和mac上没有,用其它方法完成
echo "1,2,3,4" | tr , ' '
-I
指定替换字符串:
# replace
echo "1 2 3 4" | xargs -n 1 -I {} find {}.txt
find
结合xargs
:
# 查找并删除
find . -type f -name "*.tmp" -print0 | xargs -0 rm -f
这里的-print0
和xargs -0
用\0
作为分隔符,避免temp file.tmp
之类的含有默认分隔符的文件名被拆成两个参数
统计代码行数:
find . -type f -name "*.sh" -print0 | xargs -0 wc -l
对一个参数执行多条命令:
# 与上面的replace作用相同
echo '1\n2\n3\n4' | (while read arg; do find $arg.txt; done)
xargs
对每个参数只能执行一条命令,改用子shell中循环读取的话,能在循环体里执行多条命令
P.S.这里的括号是圆括号扩展运算符,开子shell执行括号里的命令,不是前面的条件分组,不要转义括号
tr(translate)
对来自标准输入的字符进行替换,删除和压缩,用来做字符串处理
# 大小写转换
echo 'Ho Hoho hoho' | tr 'a-z' 'A-Z'
如果两个字符集合大小不一样,就把后一个集合用其最后一个字符补足,例如:
# 结果是ABC XXX
echo 'abc xyz' | tr 'a-z' 'A-X'
P.S.定义字符集合的形式是起始字符-终止字符
,结果不是一个连续的字符序列的话,就当做3个普通字符
注意:tr
只是对输入的每个字符做映射,没有串匹配和替换,是字符级的操作,不是字符序列(字符串)级的
其它常用选项:
# -d删除字符
# 结果是a, a , 579
echo 'hohoa, hoa 123, 4579' | tr -d 'ho0-4'
# -c得到补集,一般与-d结合删除补集里的字符,只保留给定字符集合里的
# 结果是hohoho1234
echo 'hohoa, hoa 123, 4579' | tr -d -c 'ho0-4'
# -s压缩字符(把连续的重复字符换成一个)
# 结果是ha, ha
echo 'hhhhhha, ha' | tr -s 'a-z'
用字符类(character class)作为集合:
# 大小写转换
echo '124abcX1' | tr '[:lower:]' '[:upper:]'
其它字符类可以通过man tr
查看
md5sum, sha1sum
这两个命令用来计算校验和,例如:
# 求文件md5
# 结果是`32个字符的16进制串 文件名`
md5sum test.sh
P.S.mac默认没有md5sum, sha1sum
,需要额外安装
用md5文件校验
# 用md5文件检查文件是否正确
md5sum -c file.md5
用md5deep
生成文件夹的md5
,需要额外安装(sha1deep
与之类似):
# yum安装
yum install md5deep
# 求文件夹的md5
# -r递归,-l生成相对路径(默认是绝对路径)
md5deep -rl dir > dir.md5
# 用所有md5文件校验
md5sum *.md5
sort & uinq
sort
命令对行排序,uniq
去重,一般配合使用,例如:
# 对file.txt内容每行按字典序排序,并去重
sort file.txt | uniq
# 或者
sort -u file.txt
默认按字典序序升序排序,-n
按数值排序,-r
降序:
# 如果字母数字都有,字母在前
sort -n file.txt
sort -r file.txt
其它常用选项:
# 检查文件内容是否有序,是否按数值序用-nC
# 返回值为0,表示有序
sort -C file.txt; echo $?
# 按第2列排序
sort -k 2 file.txt
# 按第2个字符到第5个字符排序
sort -k 2,5 file.txt
# 用\0作为分隔符(通过管道结合其它命令时有用)
sort -z file.txt
# 忽略前导空白字符
sort -b file.txt
uniq
命令只能用于有序的输入,所以一般结合sort
使用:
# 只显示唯一的行(出现多于1次的行都被滤掉)
uniq -u sorted.txt
# 统计各行出现次数
uniq -c sorted.txt
# 找出重复的行
uniq -d sorted.txt
去重也可以指定key
:
# -s跳过前几个字符,-w指定key的长度
uniq -s 3 -w 2 sorted.txt
P.S.mac没有-w
选项
split
split
命令用来分割大文件,例如:
# 把data.txt分割成1k的多个文件
split -b 1k data.txt
默认生成xaa, xab, xac...
之类的文件名,默认严格按大小拆分,行可能会被截断,甚至一个汉字被拆开
生成的文件名可以手动指定,最后一个参数是前缀,默认是x
,-a
指定后缀长度,其它选项请查看man split
也可以按行分割文件:
# 每个文件10行,文件名为`small.aa, small.ab, small.ac...`
split -l 10 test.sh 'small.'
P.S.原来有这种命令,当时为了拆分sql
备份文件,特意找了一个能够编辑大文件的文本编辑器,手动分割的…
P.S.另一个更强大的文件分割命令是csplit
,常用来分割日志文件,能够以是否存在指定文本内容为条件拆分
其它小技巧
临时文件命名
Ubuntu,Debian中有tempfile
命令,用来生成临时文件名(一个随机字符串),其它环境可以使用RANDOM
环境变量,或者当前进程id:
# 取RANDOM环境变量的值
$RANDOM
# 取当前进程id
$$
字符串提取
%, %%, #, ##
操作符提供了强大的字符串提取功能:
file=logo.png
# 提取文件名
filename=${file%.*}
echo filename:$filename
# 提取扩展名
ext=${file##*.}
echo ext:$ext
用法如下:
# 从var的值中删掉%右侧通配符所匹配的字符串,从右边向左匹配
${var%.*}
# %%贪婪匹配,会找出最长串,%匹配最短串
${var%.*}
# 从var的值中删掉#右侧通配符所匹配的字符串,从左向右匹配
${var#*.}
# 对应的贪婪匹配
${var##*.}
提取扩展名应该用##
贪婪匹配,因为file.txt.md5
之类的文件名含有多个.