用cat进行拼接
- cat(concatenate)是命令行玩家首先必须学习的命令之一,它通常用于读取、显示或拼接文件内容,不过cat的能力远不止如此,它甚至可以一次性将来自stdin和文件的内容拼接到一起。
$ cat file1 file2 file3 ... #将多个文件内容拼接在一起
- cat不仅可以读取文件、拼接数据,还能够从标准输入中进行读取,从标准输入中读取需要使用管道操作符。
$ OUTPUT_FROM_SOME COMMANDS | cat
- 我们可以用cat将来自输入文件的内容与标准输入拼接在一起,将stdin和另一个文件中的数据结合起来。
$ echo 'Text through stdin' | cat - file.txt # - 被作为stdin文本的文件名
$ cat -s file
# 删除额外的空白行(相邻的多个空白行只保留一个)。$ cat -T file.py
# 将制表符显示为^|。$ cat -n lines.txt
# 显示行号(包括空白行,如果想跳过空白行,可以使用选项-b
)。
录制并回放终端会话
# -t选项用于将时序数据导入stderr,2>用于将stderr重定向到timing.log,时序信息记录了每个命令在何时运行;output.session文件用于存储命令输出。
script -t 2> timing.log -a output.session
type commands;
'''
..
exit
# scriptreplay用于回放,按播放命令序列输出
scriptreplay timing.log output.session
文件查找与文件列表
- find是Unix/Linux命令行工具箱中最棒的工具之一。其工作方式为:沿着文件层次结构向下遍历,匹配符合条件的文件,执行相应的操作。
$ find bash_path
#列出当前目录及子目录下所有的文件和文件夹
- 如:
$find . -print
#.
指定当前目录,..
指定父目录。-print
指明打印出匹配文件的文件名(路径)。当使用-print
时,\n
作为用于对输出的文件名进行分隔。就算忽略-print
,find命令仍会打印出文件名。 -print0
指明使用\0
作为匹配的文件名之间的定界符。当文件名中包含换行符时,这个方法就有用武之地了。
- 如:
- 根据文件名或正则表达式进行搜索
$ find /home/slynux -name "*.txt" -print
#选项-name
的参数制定了文件名所必须匹配的字符串,我们可以将通配符作为参数使用。$ find . -iname "example*" -print
#选项-iname
的作用和-name
类似,只不过在匹配名字时会忽略大小写。$ find . \( -name "*.txt" -o -name "*.pdf" \) -print
#匹配多个条件$ find /home/users -path "*/slynux/*" -print
#选项-path
的参数可以使用通配符来匹配文件路径。-name
总是用给定的文件名进行匹配,-path
则将文件路径作为一个整体进行匹配。$ find . -regex ".*\(\.py|\.sh\)$"
# 基于正则表达式来匹配文件路径$ find . -iregex ".*\(\.py|\.sh\)$"
# 忽略大小写。
- 否定参数。
find
也可以用!
否定参数的含义,如:
$ find . ! -name "*.txt" -print
#匹配所有不以.txt
结尾的文件名。
- 基于目录深度的搜索。find命令在使用时会遍历所有的子目录,我们可以采用深度选项
-maxdepth
和-mindepth
来限制find命令遍历的目录深度。
- 大多数情况下,我们只需要在当前目录中进行搜索,无须再继续向下查找。如果只允许find在当前目录中查找,深度可以设置为1;当需要向下两级时,深度可以设置为2;依此类推。
$ find . -maxdepth 1 -name "f*" -print
#只搜索当前目录$ find . -mindepth 2 -name "f*" -print
#从第二级目录开始搜索
- 根据文件类型搜索
$ find . -type d -print
#只列出所有的目录$ find . -type f -print
#只列出普通文件$ find . -type l -print
#只列出符号链接$ find . -type c -print
#只列出字符设备$ find . -type b -print
#只列出块设备$ find . -type s -print
#只列出套接字$ find . -type p -print
#只列出FIFO
- 根据文件时间进行搜索
- 按天:访问时间
-atime
;修改时间-mtime
;变化时间-ctime
- 按分钟:访问时间
-amin
;修改时间-mmin
;变化时间-cmin
$ find . -type f -atime 7 -print
#打印出恰好在7天前被访问过的所有文件$ find . -type f -atime -7 -print
#打印出在最近7天内被访问过的所有文件$ find . -type f -atime +7 -print
#打印出访问时间超过7天的所有文件$ find . -type f -amin +7 -print
#打印出访问时间超过7分钟的所有文件$ find . -type f -newer file.txt -print
#打印出比file.txt修改时间更近的所有文件
- 按天:访问时间
- 基于文件大小的搜索
$ find . -type f -size +2k
#大于2KB的文件$ find . -type f -size -2k
#小于2KB的文件$ find . -type f -size 2k
#大小等于2KB的文件- 除了k之外,还可以用其他文件大小单元。
- b —— 块(512字节)
- c —— 字节
- w —— 字(2字节)
- k —— 1024字节
- M —— 1024K字节
- G —— 1024M字节
- 删除匹配的文件
$ find . -type f -name "*.swp" -delete
- 基于文件权限和所有权的匹配
$ find . -type f -perm 644 -print
#打印出权限为644的文件$ find . -type f ! -perm 644 -print
#打印出权限没有设置为644的文件$ find . -type f -user slynux -print
#打印出用户slynux拥有的所有文件
- 利用find执行命令或动作
- find命令可以借助选项
-exec
与其他命令进行结合,-exec
算的上是find最强大的特性之一。 $ find . -type f -user root -exec chown slynux {} \;
#{}
是一个与-exec
选项搭配使用的特殊字符串。对于每一个匹配的文件,{}
会被替换成相应的文件名。$ find . -type f -name "*.c" -exec cat {} \;>all_c_file.txt
#将给定目录中所有C程序文件拼接起来写入单个文件$ find . -type f -mtime +10 -name "*.txt" -exec cp {} OLD \;
#将10天前的txt文件复制到OLD目录中。$ find . -type f -name "*.txt" -exec printf "Text file: %s\n" {} \;
#-exec
可以同printf
结合来生成有用的输出信息。-exec
无法结合多个命令,但是我们可以将多个命令写到一个shell脚本中(如command.sh),然后在-exec
中使用这个脚本:-exec ./command.sh {} \;
- find命令可以借助选项
- 让find跳过特定的目录。在搜索目录并执行某些操作时,有时为了提高性能,需要跳过一些子目录。
$ find devel/source_path \( -name ".git" -prune \) -o \( -type f-print \)
#打印出不包括在.git
目录中的所有文件的名称。
玩转xargs
- 我们可以用管道将一个命令的stdout重定向到另一个命令的stdin,如:
cat foo.txt | grep "test"
。但是有些命令只能以命令行参数的形式接收数据,无法通过stdin接受数据流。 - 我们可以使用
xargs
将标准输入数据转换为命令行参数;xargs
也可以将单行或多行文本输入转换成其他格式,例如单行变多行或是多行变单行。 xargs
命令应该紧跟在管道操作符之后,以标准输入作为主要的源数据流。它使用stdin并通过提供命令行参数来执行其他命令。如:command | xargs
。$ cat example.txt | xargs
#单行输出$ cat example.txt | xargs -n 3
#每行最多3个参数,多行输出,参数间由空格隔开。$ echo "splitXsplitXsplitXsplit" | xargs -d X
#用-d
选项为输入指定一个定制的定界符。$ echo "splitXsplitXsplitXsplit" | xargs -d X -n 2
#结合-d
和-n
选项。读取stdin,将格式化参数传递给命令。
#!/bin/bash # 文件名:cecho.sh echo $*'#'
有一个包含着参数列表的文件(每行一个参数),通过两种方法传递给一个命令(cecho.sh),第一种方法,每次提供一个参数,如:
./cecho.sh arg1 ./cecho.sh arg2 ./cecho.sh arg3
我们可以这样写:
$ cat args.txt | xargs -n 1 ./cecho.sh
第二种方法,一次性提供所有参数,如:
./cecho.sh arg1 arg2 arg3
有时我们还需要一些固定不变的命令参数,如:
./cecho.sh -p arg1 -l
xargs有一个选项
-I
,可以提供上面这种形式的命令执行序列。我们可以通过-I
指定替换字符串,这个字符串在xargs扩展时会被替换掉,如果将-I
与xargs 结合使用,对于每个参数,命令都会被执行一次。如:
$ cat args.txt | xargs -I {} ./cecho.sh -p {} -l
结合find使用xargs。xargs和find是一对死党,但是人们通常却是以一种错误的组合方式使用他们。 如:
$ find . -type f -name "*.txt" -print | xargs rm -f
。这种做法很危险,因为我们无法预测分隔find命令输出结果的定界符究竟是什么(
\n
或者),很多文件名可能包含空格,因此xargs可能会误认为它们是定界符,如
hell text.txt
会被xargs误解为hell和text.txt。只要我们把find的输出作为xargs的输入,就必须将
-print0
与find结合使用,以字符null(\0)
来分隔输出。$ find . -type f -name "*.txt" -print0 | xargs -0 rm -f
#xargs -0
将\0
作为输入定界符。统计源代码目录中所有C程序文件的行数。
$ find source_code_dir_path -type f -name "*.c" -print0 | xargs -0 wc -l
结合stdin,巧妙运用while语句和子shell。xargs只能以有限的几种方式来提供参数,而且它也不能为多组命令提供参数,要执行包含来自标准输入的多个参数的命令,有一种非常灵活的方法。包含while循环的子shell可以用来读取参数,然后通过一种巧妙的方式执行命令:
$ cat files.txt | ( while read arg; do cat $arg; done )
等同于
$ cat files.txt | xargs -I {} cat {}
在while循环中,我们可以将
cat $arg
替换成任意数量的命令,这样我们就可以对同一个参数执行多条命令。也可以不借助管道,将输出传递给其他命令。这个技巧能够使用于各种问题场景,子shell操作符内部的多个命令可以作为一个整体来运行。$ cmd0 | ( cmd1;cmd2;cmd3 ) | cmd4