find
find 命令会沿着文件层次向下遍历,匹配符合条件的文件,然后执行响应的命令。
列出某个目录下的所有文件
find ./src -print
-print
是默认的,对输出的文件名使用 n
分隔,也可以使用 -print0
这个时候会使用 0
作为分隔符。
使用文件名来搜索
-name
选项的参数指定了文件名中必须包含的字符串。文件名中可以包含通配符。
find ./src -name "*.js" -print
这会打印出所有在 src
目录下的 js 文件
使用 -iname
,忽略大小写,其他同 -name
从 path 中进行搜索
使用 name 选项之会考虑文件名,使用 -path
选项后,会在在文件的完整路径中进行匹配
find ./src -path "*/css/*"
搜索 src 下面 css 目录中的所有文件
使用正则来搜索
使用正则表达式,根据文件的完整路径来进行搜索
find ./src -regex ".*\.md$"
寻找到所有的 markdown 文化(以 .md
结尾),注意这里的正则表达式需要完全匹配文件路径才行,而不是只匹配其中的一部分。
使用 -iregex
,忽略大小写,其他同 -regex
否定参数
find ./src ! -name "*.js"
使用一个 !
来将条件置反,以上命令搜索 src
下不是 js 的文件
所有的选项都可以使用 !
来取反,只需要将其放在选项前面:
find . ! -name "*.js" -atime +7 ! -type f
基于目录深度
使用 -mindepth
和 -maxdepth
来指定最小或最大的搜索深度
根据文件类型搜索
使用 -type
选项搜索指定类型的文件。这里的类型也就是 ls -l
命令打印出的文件列表的第一个字符。
-rw-r--r-- 1 wangyu staff 1857 8 21 16:58 style.css
drwxr-xr-x 4 wangyu staff 136 11 16 21:35 typings
-rw-r--r-- 1 wangyu staff 87 11 16 21:35 typings.json
drwxr-xr-x 4 wangyu staff 136 11 29 20:28 webpack
-rw-r--r-- 1 wangyu staff 612 12 1 15:55 yarn.lock
根据文件时间进行搜索
-atime
:最后一次访问文件的时间-mtime
:最后一次修改文件的时间-ctime
:文件元数据最后一次被修改的时间
这些选项的参数通常使用整数指定,并带一个 -
(表示小于) 或 +
(表示大于)。
比如:
找出七天内被访问过的文件
find . -type f -atime -7
-atime
,-mtime
,-ctime
都是以天为单位的,可以使用 -amin
,-mmin
,-cmin
来以分钟为单位进行搜索。
另外,还可以使用 -newer
选项来指定一个文件,使用找出被这个文件的修改时间更近的其他文件:
# 找出所有比 `file1.txt` 修改时间更晚的文件。
find . -newer file1.txt
基于文件大小的搜索
使用 -size
选项可以指定满足条件的文件大小。
# 找到所有小于 2k 的 js 文件
find . -size -2k -name "*.js"
除了 k 以外,还可以指定其他单位:
b:磁盘上的一块,512 字节
c:字节
w:字,2字节
k:1024 字节
M:1024k
G:1024M
删除匹配的文件
使用 -delete
选项可以删除搜索到的文件
find . -name "*.swp" -delete
基于文件权限来搜索
-perm
选项可以根据文件权限进行匹配
# 找出文件权限为 644 的文件
find . -perm 644
使用 -user
选项还可以搜索某个用户的文件
对 find 得到的结果执行命令
比如希望统计所有满足 find 条件的文件的行数,使用 -exec
选项可以轻松做到:
find . -name "*.sh" -exec wc -l {} \;
对于每一个搜索结果,这里的 {}
都会被替换为相应的文件名,而后面的 ;
则表示 exec 参数的结束,之所以需要加 ` 是为了转译,在 shell 中,
; 是有特殊意义的。你也可以将
; 换做
';'`,效果一样,都是不让 shell 对它进行转译。
跳过某些目录
find . \( -name ".git" -prune \) -o \( -type f -print \)
这里使用圆括号和 -o
分了两个组,-prune
是指去除满足该分组条件的文件,而 -print
是指打印满足该分组条件的文件。
cat
cat 用于输出一个文本中的内容,或者使用管道输出标准输出中的内容,最常见的用法如下:
cat file1.txt file2.txt
echo 'hello' | cat
cat 还有其他的一些技巧:
组合标准输出和文件的内容
要将标准输入的内容和另外一个文件的内容拼起来得到一个新的文件
ls | cat - file1.txt > file2.txt
这里的 -
是一个占位符,表示标准输出,这样标准输出的内容和 file1.txt 的内容就被组合起来添加到 file2.txt 中了。
删除多余空行
加上 -s
选项,相邻的空白行会被压缩
打印行号
-n
选项可以打印行号。-b
选项也可以打印行号,且会跳过空白行。
使用特殊字符表示制表符
-t
选项,可以将制表符打印为 ^I
便于发现这些隐藏符号。
xargs
利用管道可以轻松地将标准输出重定向至标准输入。比如:
cat main.cpp | wc -l
但是,有的时候希望将标准输出作为某个命令的参数传入。比如:
一个文件(list.txt)中有以下内容,其中包含的是一些文件名,现在希望统计这些文件的行数
ex1.sh
ex2.sh
ex3.sh
希望执行的命令应该是 wc -l ex1.sh ex2.sh ex3.sh
,使用管道肯定是不能达成希望的。
这个时候就需要使用 xargs 命令了,这个命令可以轻松地将标准输入进行一些转换,然后传给一个命令作为参数,xargs 以标准输入作为数据源。因此常见的用法是结合管道使用,上面案例使用 xargs 的解法如下:
cat list.txt | xargs wc -l
这样 list.txt 中的内容,就作为 wc 命令的参数传入了。xargs 可以对传入命令的参数做更精细的控制:
格式化参数
将多行输入转换为单行输出
上面的 list.txt
中的内容是多行的,经过了 xargs 之后就变成单行的了。
$ cat list
ex1.sh
ex2.sh
ex3.sh
$ cat list | xargs
ex1.sh ex2.sh ex3.sh
将单行输入转换为多行输出
使用 -n
选项可以指定每行的最大参数数量,这里会使用默认定界符(空格)来将标准输入进行切分,然后每行显示指定数量的参数。
$ cat line.txt
1 2 3 4
$ cat line.txt | xargs -n 2
1 2
3 4
读取 stdin 将其进行格式化后传给命令
使用 -n
选择,可以指定每次传给命令的最大参数个数:
$ echo "1 2 3 4" | xargs -n 2 echo # 每次传入最多两个参数
1 2
3 4
$ echo "1 2 3 4" | xargs -n 3 echo # 每次传入最多 3 个参数
1 2 3
4
很多时候,一个命令还需要传入其他的一些选项,而且参数的传入顺序是固定的,这个时候可以使用占位符
$ echo "1 2 3 4" | xargs -n 1 -I {} printf "%s --> %s\n" {} {}
1 --> 1
2 --> 2
3 --> 3
4 --> 4
这里 -I
指定了占位符号 {}
后面所有的 {}
都会被参数所代替,这里的 {}
并没有什么特别意义,可以使用 -I
指定其他字符。
结合 find 使用 xargs
xargs + find 可以做很多有用的事情,但要注意别使用错误的方法
find . -type f -name "*.txt" -print | xargs rm -f
这个时候如果文件名中存在空格,比如 lib xxx.md
,xargs 使用空格作为定界符,就会将它误认为是 lib
和 xxx.md
,因此在 find 中要使用 -print0
来输出,在 xargs 中要使用 0
来作为定界符。
find . -type f -name "*.txt" -print0 | xargs -0 rm -f
在 xargs 指定 -0
就可以指定其使用 0
来作为定界符。
题外话
在 shell 中使用括号括起来的命令会在子 shell 中执行:
ls ; (cd ..; ls); ls
括号中的 cd 之后改变括号里面的语句执行时的目录,而不会影响第三个 ls 执行时候的目录。
tr
tr 命令用来转换字符,它将字符从一个集合映射到另外一个集合。其基本用法如下:
tr [options] charset1 charset2
将 charset1 中的字符转换为 charset2 中对应的字符,需要注意的是 charset1 和 charset2 的长度理论上应该相等,这样才能实现字符的一一映射。
如果 charset1 的长度大于 charset2,那么 charset2 会重复最后一个字符,直到长度相等。如果 charset2 的长度大于 charset1,那么多出来的字符会被忽略掉。
将字符串转换为大写
echo "hello world" | tr "a-z" "A-Z"
a-z
, A-Z
, 0-9
, A-Z0-9
这都是合法的字符集
用 tr 删除字符
使用 -d 选项,可以删除集合中的字符。
$ echo 'A1B2C3' | tr -d "1-9"
ABC
使用字符补集来删除字符
使用 -c 选项,可以指定一个字符集,配合 -d 使用,会只保留集合中的字符
$ echo 'A1B2C3' | tr -d -c "A-Z"
ABC
删除重复字符
使用 -s 选项,可以指定一个字符集,tr 会将存在于该字符集中的重复字符压缩至 1 个。
$ echo 'AAA-BBB-C' | tr -s "AB"
A-B-C
tr 还可以使用字符类来表示集合
[:alnum:]
:字母和数字[:alpha:]
:字母[:cntrl:]
:控制(非打印)字符[:digit:]
:数字[:graph:]
:图形字符[:lower:]
:小写字母[:print:]
:可打印字符[:punct:]
:标点符号[:space:]
:空白字符[:upper:]
:大写字母[:xdigit:]
:十六进制字符
用法:
echo "AB" | tr "[:upper:]" "[:lower:]"
sort
sort 用来对文本中的每一行进行排序。
示例:
# 默认排序
sort file.txt
# 按数学顺序排序
sort -n file.txt
# 逆序排序
sort -r file.txt
# 判断文件是否经过排序
sort -c file.xtt
根据键或列来排序
常常看到这样的文本内容,希望以每行的数字来进行排序
james 23
kobe 24
wade 3
这类文件的内容,使用分隔符分为了多列,利用 sort 的 -k
选项可以指定以第几列作为排序的依据:
# 依据第二列,按数字顺序排序
cat list.txt | sort -nk 2
wade 3
james 23
kobe 24
有的时候文本并没有使用空格隔成列,比如:
b78434
s89988
c89878
这是一列列编号,假如希望以其中第 2 和第 3 个字符来进行排序,可以这样:
cat No.txt | sort -nk 2,3
这里的 2,3
指定了起始位置。
uniq
uniq 用来消除重复的行。它只能接受排序后的内容作为输入,基本用法如下:
sort file.txt | uniq
显示唯一的行
使用 -u
选项,只显示唯一的行,而不是多行被压缩至一行。
显示重复次数
使用 -c
选项,可以显示每一行重复出现的次数。
找出重复的行
使用 -d
选项,可以找到那些重复出现的行。
指定比较的区间
如果 sort 可以使用 -k 选项指定每行中要进行比较的内容一样,uniq 也可以指定,它是通过 -s
和 -w
来完成的
-s
:指定可以跳过前 n 个字符-w
:指定用于比较的最大字符数
mktemp
这个命令用来生成临时文件或临时文件夹,常常需要一个临时文件来保存一些内容,这个时候该命令就很有用了。
# 创建临时文件
file=`mktemp`
# 创建临时目录
dir=`mktemp -d`
# 仅仅生成文件名,而不实际创建文件或目录
tmpfile=`mkdir -u`
# 根据模板来创建,这里的 XXX 会被替换为随机字符,至少要保证存在 3 个及 3 个以上的 X
mktemp test.XXX
生成任意大小的文件
dd 命令可以用来生成指定大小的文件,其使用方法如下:
dd if=/dev/zero of=junk.data bs=1M count=1
其中各个字段的意思是:
if:input file,默认 stdin
of:output file,默认 stdout
bs:块大小,可以是 1k 1G 等
count:需要多少块
文本文件的交集和差集
comm 命令用来比较两个排过序的文件
设置文件权限
# 设置权限
$ chmod u=rwx g=rw o=r filename
$ chmod 764 filename
# 给某类用户增加或取消某个权限
$ chmod o+x filename
$ chmod u-x filename
o: other
u: user
g: group
a: all
设置粘滞位
粘滞位是一种给目录设置的权限类型,通过给目录设置粘滞位,目录内的文件只有目录所有者才能删除。
$ chmod a+t dir_name
递归地设置权限
使用 -R
选项指定以递归的方式修改权限:
$ chmod 777 . -R
链接
链接分为软连接和硬链接,软链接又被称为符号链接。
软链接
软链接包含一个绝对路径,指向另外一个文件,被指向的文件可以不存在。被指向的文件被删除后,软连接还存在,只是打不开什么东西了,这和 Windows 中的快捷方式的概念类似。
使用如下命令创建软链接:
ln -s app.js app.link.js
然后使用 ls -l
就能看到:
lrwxr-xr-x 1 xxx yyy 5B 12 11 14:06 app.link.js -> app.js
这里指 app.link.js
是 app.js
的符号链接。
硬链接
硬链接存储的是文件的 inode 号,inode 中记录着文件内容在磁盘上存放的位置。因此硬链接实际上是给文件创建了多个别名,每创建一个硬链接,文件的 inode 引用数量就会加 1,这个数量可以在 ls -l
得到的结果的第二项中看到,当一个文件删除了以后,这个数量就会减小,直到该数量减到 0 ,文件才会被删除。因此创建了硬链接后删除原文件,不会对链接文件造成影响。甚至可以说它们是相同的,不存在谁是谁的链接这种说法,他们都保存了 inode,通过 inode 来获取到文件信息。
head
# 打印前十行
head file
# 打印前 m 行
head -n m file
tail
# 打印最后十行
tail file
# 打印后 m 行
tail -n m file
只列出目录
# 利用目录后的斜杠
ls -F | grep "/$"
# 利用文件的类型
ls -l | grep "^d"
# 使用 find 的 type 选项
find . -type d -maxdepth 1 -print
使用 pushd 和 popd 来切换目录
在命令行中如果要经常切换目录,恰恰这两个目录又不是父子关系的时候,可能每次都要键入很长的目录。使用 pushd 和 popd 可以将目录压入栈中,便于快速切换。
# 压栈并切换目录
pushd /var/www
# 回到栈中的第 2 个(从 0 开始计数)目录
push +1
# 查看栈中的内容
dirs
# 回到堆栈中的上一个目录
popd
# 回到之前的目录
cd -
wc
# 统计行数
wc -l file
# 统计单词数
wc -w file
# 统计 byte 数
wc -c file
# 统计字符数,涉及中文字符的时候使用这个才准确
wc -m file
打印目录树
# 只打印目录
tree . -d
# 打印出文件大小
tree . -h
wait
通过让任务在后台运行,然后拿到他们的 PID,将 PID 传给 wait 命令,可以利用计算机的多个核心来进行运算:
PIDARRAY=()
for item in $list
do
zip $item &
PIDARRAY+=("$!")
done
wait ${PIDARRAY[@]}
$!
保存着最近一个后台进程的 PID