常用简单命令_bash笔记2

2023-03-15 18:45:18 浏览数 (3)

感谢支持ayqy个人订阅号,每周义务推送1篇(only unique one)原创精品博文,话题包括但不限于前端、Node、Android、数学(WebGL)、语文(课外书读后感)、英语(文档翻译) 如果觉得弱水三千,一瓢太少,可以去 http://blog.ayqy.net 看个痛快

cat(concatenate)

读取、显示、拼接文件内容

把来自标准输入的内容和文件内容拼接起来:

代码语言:javascript复制
echo 'from stdin' | cat - test.sh

cat命令用-表示标准输入

If file is a single dash (`-‘) or absent, cat reads from the standard input.

其它常用功能选项:

代码语言:javascript复制
# 给文件内容添上行号
cat -n test.sh
# 把文件中的连续多个空行压缩成一个
cat -s test.sh

find

基本规则

从文件目录向下遍历,匹配符合条件的,处理

代码语言:javascript复制
# 列出当前目录下所有文件/文件夹、子文件/文件夹
find .
# 用分隔(文件路径里有换行符时有用)
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

按文件类型搜索

代码语言:javascript复制
# 指定文件/文件夹,-type f表示只输出文件
find ../tnode ! -regex ".*node_modules.*" -type f

P.S.参数顺序会影响搜索效率,比如先检查深度再过滤类型更快

文件类型与type参数值对应关系:

代码语言:javascript复制
普通文件:f
符号链:l
目录:d
面向字符的设备文件:c
面向块的设备文件:b
套接字:s
FIFO:p

按时间搜索

每个文件有3种时间戳:

代码语言:javascript复制
访问时间:-atime
修改时间:-mtime
变化时间:-ctime

参数值为整数,表示天数,可以前缀 -,分别表示大于,小于,例如:

代码语言:javascript复制
# 找出父级目录中,昨天到现在访问过的文件
find .. -type f -atime -1

也有以分钟为单位的:

代码语言:javascript复制
# -amin, -mmin, -cmin
find .. -type f -amin $((-1 * 60 * 24))

还可以指定一个文件作为参照,找出更新的(修改时间更近的)文件:

代码语言:javascript复制
# 找出父级目录中,比~/.bash_profile更新的文件
find .. -type f -newer ~/.bash_profile

按文件大小搜索

代码语言:javascript复制
# 当前目录下大于1K的文件
find . -type f -size  1k

支持b块, c字节, w字, k, M, G单位,注意前面小写,后两个大写,在其它命令里一般也是这样,例如split

其它用法

代码语言:javascript复制
# 查找并删除
find . -type f -name "*.tmp" -delete
# 匹配文件权限
find . -type f -perm 777 -print
find . -type f -user ayqy

-exec结合执行其它命令

代码语言:javascript复制
# 查找并格式化输出
find . -type f -exec printf "file: %sn" {} ;
# 查找并备份
find . -type f -mtime  7 -exec cp {} bak/ ;

P.S.末尾转义分号用来表示-exec参数值结束,必须要有

-exec只能执行一条命令,需要执行多条的话,把命令写入文件再执行,例如把备份命令写入bak.sh

代码语言:javascript复制
#!/bin/bash
BAK_DIR=bakif ! test -e "$BAK_DIR";
then
   mkdir "$BAK_DIR"
fifor file in "$@";
do
   cp $file "$BAK_DIR"
done

再查找执行:

代码语言:javascript复制
find . -type f -mtime  7 -exec ./bak.sh {} ;

-prune排除不需要查找的东西:

代码语言:javascript复制
# 跳过.git和node_moudles目录
find . ( -name ".git" -prune ) -o ( -name "node_modules" -prune ) -o ( -type f -print )

xargs

xargs命令把从stdin接到的数据重新格式化,作为参数提供给其它命令,紧跟在管道操作符之后,基本形式:

代码语言:javascript复制
cmd | xargs

把多行输入转换成单行输出:

代码语言:javascript复制
# 把换行符换成空格
cat test.sh | xargs

把单行输入转换成多行输出:

代码语言:javascript复制
# 按每行参数数量断开
echo "1 22 3 4 5 6 7" | xargs -n 3

-d指定分隔符,实现字符串split

代码语言:javascript复制
# split
echo "1,2,3,4" | xargs -d ,
# `-d`参数是GUN扩展,FreeBSD和mac上没有,用其它方法完成
echo "1,2,3,4" | tr , ' '

-I指定替换字符串:

代码语言:javascript复制
# replace
echo "1 2 3 4" | xargs -n 1 -I {} find {}.txt

find结合xargs

代码语言:javascript复制
# 查找并删除
find . -type f -name "*.tmp" -print0 | xargs -0 rm -f

这里的-print0xargs -0作为分隔符,避免temp file.tmp之类的含有默认分隔符的文件名被拆成两个参数

统计代码行数:

代码语言:javascript复制
find . -type f -name "*.sh" -print0 | xargs -0 wc -l

对一个参数执行多条命令:

代码语言:javascript复制
# 与上面的replace作用相同
echo '1n2n3n4' | (while read arg; do find $arg.txt; done)

xargs对每个参数只能执行一条命令,改用子shell中循环读取的话,能在循环体里执行多条命令

P.S.这里的括号是圆括号扩展运算符,开子shell执行括号里的命令,不是前面的条件分组,不要转义括号

tr(translate)

对来自标准输入的字符进行替换,删除和压缩,用来做字符串处理

代码语言:javascript复制
#  大小写转换
echo 'Ho Hoho hoho' | tr 'a-z' 'A-Z'

如果两个字符集合大小不一样,就把后一个集合用其最后一个字符补足,例如:

代码语言:javascript复制
# 结果是ABC XXX
echo 'abc xyz' | tr 'a-z' 'A-X'

P.S.定义字符集合的形式是起始字符-终止字符,结果不是一个连续的字符序列的话,就当做3个普通字符

注意:tr只是对输入的每个字符做映射,没有串匹配和替换,是字符级的操作,不是字符序列(字符串)级的

其它常用选项:

代码语言:javascript复制
# -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)作为集合:

代码语言:javascript复制
# 大小写转换
echo '124abcX1' | tr '[:lower:]' '[:upper:]'

其它字符类可以通过man tr查看

md5sum, sha1sum

这两个命令用来计算校验和,例如:

代码语言:javascript复制
# 求文件md5
# 结果是`32个字符的16进制串 文件名`
md5sum test.sh

P.S.mac默认没有md5sum, sha1sum,需要额外安装

用md5文件校验

代码语言:javascript复制
# 用md5文件检查文件是否正确
md5sum -c file.md5

md5deep生成文件夹的md5,需要额外安装(sha1deep与之类似):

代码语言:javascript复制
# yum安装
yum install md5deep
# 求文件夹的md5
# -r递归,-l生成相对路径(默认是绝对路径)
md5deep -rl dir > dir.md5
# 用所有md5文件校验
md5sum *.md5

sort & uinq

sort命令对行排序,uniq去重,一般配合使用,例如:

代码语言:javascript复制
# 对file.txt内容每行按字典序排序,并去重
sort file.txt | uniq
# 或者
sort -u file.txt

默认按字典序序升序排序,-n按数值排序,-r降序:

代码语言:javascript复制
# 如果字母数字都有,字母在前
sort -n file.txt
sort -r file.txt

其它常用选项:

代码语言:javascript复制
# 检查文件内容是否有序,是否按数值序用-nC
# 返回值为0,表示有序
sort -C file.txt; echo $?
# 按第2列排序
sort -k 2 file.txt
# 按第2个字符到第5个字符排序
sort -k 2,5 file.txt
# 用作为分隔符(通过管道结合其它命令时有用)
sort -z file.txt
# 忽略前导空白字符
sort -b file.txt

uniq命令只能用于有序的输入,所以一般结合sort使用:

代码语言:javascript复制
# 只显示唯一的行(出现多于1次的行都被滤掉)
uniq -u sorted.txt
# 统计各行出现次数
uniq -c sorted.txt
# 找出重复的行
uniq -d sorted.txt

去重也可以指定key

代码语言:javascript复制
# -s跳过前几个字符,-w指定key的长度
uniq -s 3 -w 2 sorted.txt

P.S.mac没有-w选项

split

split命令用来分割大文件,例如:

代码语言:javascript复制
# 把data.txt分割成1k的多个文件
split -b 1k data.txt

默认生成xaa, xab, xac...之类的文件名,默认严格按大小拆分,行可能会被截断,甚至一个汉字被拆开

生成的文件名可以手动指定,最后一个参数是前缀,默认是x-a指定后缀长度,其它选项请查看man split

也可以按行分割文件:

代码语言:javascript复制
# 每个文件10行,文件名为`small.aa, small.ab, small.ac...`
split -l 10 test.sh 'small.'

P.S.原来有这种命令,当时为了拆分sql备份文件,特意找了一个能够编辑大文件的文本编辑器,手动分割的…

P.S.另一个更强大的文件分割命令是csplit,常用来分割日志文件,能够以是否存在指定文本内容为条件拆分

其它小技巧

临时文件命名

Ubuntu,Debian中有tempfile命令,用来生成临时文件名(一个随机字符串),其它环境可以使用RANDOM环境变量,或者当前进程id:

代码语言:javascript复制
# 取RANDOM环境变量的值
$RANDOM
# 取当前进程id
$$

字符串提取

%, %%, #, ##操作符提供了强大的字符串提取功能:

代码语言:javascript复制
file=logo.png
# 提取文件名
filename=${file%.*}
echo filename:$filename
# 提取扩展名
ext=${file##*.}
echo ext:$ext

用法如下:

代码语言:javascript复制
# 从var的值中删掉%右侧通配符所匹配的字符串,从右边向左匹配
${var%.*}
# %%贪婪匹配,会找出最长串,%匹配最短串
${var%.*}# 从var的值中删掉#右侧通配符所匹配的字符串,从左向右匹配
${var#*.}
# 对应的贪婪匹配
${var##*.}

提取扩展名应该用##贪婪匹配,因为file.txt.md5之类的文件名含有多个.

1 人点赞