您的位置:首页 > 运维架构 > Shell

Linux Shell脚本攻略2:命令之乐(1)

2017-09-10 16:49 176 查看

用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 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息