乐趣区

关于java:别催了别催了这篇文章我一次性把Shell的内容说完

Shell 搜寻与匹配

1、在文件中查找字符串

grep 命令能够搜寻文件,查找指定的字符串。

$ grep myvar *.c

在这个例子中,咱们搜寻的文件全都位于当前目录下。因而,咱们只应用了简略的 shell 模式 *.c 来匹配以 .c 完结的文件,并没有在文件名前再增加门路。

但并非所有待搜寻的文件都老老实实地待在当前目录下。但因为 shell 并不在意你输出多少路径名,所以咱们也能够这么写:

$ grep myvar ../lib/*.c ../server/*.c ../cmd/*.c */*.c

如果待搜寻的文件不止一个,grep 会在输入前加上文件名以及冒号,而后是该文件中蕴含 grep 搜寻内容的文本。

grep 的第一个(非选项)参数能够是一个简略的字符串,也能够是更简单的正则表达式(regexp)。正则表达式不同于 shell 的模式匹配,只管两者有时看起来差不多。

常见谬误

遗记指定 grep 的输出,例如 grep myvar。这种状况下,grep 会认为你要从 STDIN 提供输出,而你认为它会读取文件,于是 grep 就干等着,鸿鹄之志。

2、只显示蕴含搜寻后果的文件名

你须要找出蕴含特定字符串的文件,然而不想看到其所在的文本行,只用输入文件名即可,常常在线上为了搜寻配置文件。

用 grep 的 -l 选项仅显示文件名即可,如下:

$ grep -l myvar *.c
both.c
good.c
somio.c
$

如果在一个文件中找到了屡次匹配,grep 依然只输入该文件名一次。如果没有找到匹配,则什么都不输入。

因为这些文件蕴含了你要查找的字符串,如果想据此构建一个待处理

文件的列表,选项 -l 就能派上用场了。将 grep 命令放进 $(),而后就能够在命令行上应用这些文件名了,如下:

rm -i $(grep -l 'This file is obsolete' *) 

删除蕴含字符串“This file is obsolete”的文件,咱们给 rm 加上了 -i 选项,以便在删除每个文件前都先询问你。

3、不辨别大小写搜寻

你想要在日志文件中不辨别大小写地搜寻字符串(如“error”),以匹配该字符串的所有呈现。用 grep 的 -i 选项疏忽大小写,如下所示:

grep -i error logfile.log

不辨别大小写的搜寻可能找出蕴含“ERROR”、“error”、“Error”的日志音讯,“ErrOR”和“eRrOr”这样的也不例外。该选项在查找大小写混合的单词时尤其管用, 或者对于查找的内容无奈确定大小时。

4、缩减搜寻后果

如果搜寻返回的后果不合乎预期,其中包含许多并不需要的内容。将后果通过管道传给 grep -v 并用表达式形容出你不想看到的内容。假如你想在日志文件中找出整个 12 月的日志音讯。你晓得日志文件用字母缩写 Dec 代表 12 月,但不敢肯定总是如此,为了确保找出所有的日志音讯,输出下列命令:

grep -i dec logfile

失去的后果却如下所示:

...
error on Jan 01: not a decimal number
error on Feb 13: base converted to Decimal
warning on Mar 22: using only decimal numbers
error on Dec 16 : the actual message you wanted
error on Jan 01: not a decimal number
...

一种快而糙的解决方案是,将第一次失去的后果通过管道传给另一个 grep,由后者过滤掉所有的“decimal”。

grep -i dec logfile | grep -vi decimal

将多个 grep 串联在一起(因为前所未见、出其不意的匹配会一直呈现),逐渐过滤搜寻后果,直至称心,这种做法并不鲜见。

-v 选项十分不便,你只须要记住该排除什么就行了。

5、搜寻更简单的模式

grep 中的正则表达式提供了更为弱小的模式匹配性能,可能满足大部分需要。正则表达式形容了待匹配字符串的模式。字母字符(或者对于 shell 没有非凡含意的其余字符)只匹配本身。“A”匹配 A,“B”匹配 B,这没什么好说的。另一个重要的规定是按地位组合字母,如 AB 匹配“AB”。这看起来也是不言而喻的。然而,正则表达式还定义了其余一些特殊字符,它们既能够独自应用,也能够与其余字符联合,从而造成更为简单的模式。

第一个特殊字符是点号(.),它能够匹配任意单个字符。因而,…. 能够匹配任意 4 个字符;A. 匹配“A”以及紧随其后的任意单个字符;.A. 匹配任意单个字符,而后是“A”,接着是任意单个字符(未必和匹配到的第一个字符雷同)。

第二个特殊字符时星号(*), 匹配上一个字符的 0 次或屡次呈现,因而,A 匹配 0 个或多个“A”字符,.* 匹配 0 个或多个任意字符(如“abcdefg”、“aaaabc”、“sdfgf ;lkjhj”,甚至是空行)。

那么 ..* 是什么意思?它匹配任意单个字符以及紧随其后的 0 个或多个任意字符(也就是一个或多个字符,但不能是空行)。

如下所示,咱们晓得一行的某些单词,咱们想含糊匹配,如下操作:

grep -E "1.*22" 2.text  // 匹配 1 结尾,任意个字符后是 22

后果如下:

1898090808098822:  

Shell 文件查找

1、查找所有的 txt 文件

文件系统中到处都是 txt 文件。你想将它们集中到一个地位。那么咱们该如何做呢?

find 命令能够找出符合要求的所有文件并执行命令,将其挪动到指定地位。例如:

find . -name '*.txt' -print -exec mv '{}'  /txts  \;

find 命令的语法和其余 Unix 命令不同,其选项并不是那种典型的连字符加上单字母,前面再跟上若干参数。find 命令的选项看起来像是简短的单词 1,按照逻辑程序呈现,并形容要查找哪些文件以及如何解决找到的文件(如果存在的话)。这种像单词一样的选项通常称为 谓词(predicate)。

find 命令的 第一个参数 是待搜寻的目录。典型用法是用点号(.)代表当前目录,不过你也能够提供一个目录列表,甚至通过指定根目录(/)来搜寻整个文件系统(只有权限容许)。

示例中的第一个选项(谓词 -name)指定了要搜寻的文件模式。其语法和 bash 的模式匹配语法差不多,因而 *.txt 可能匹配所有以“.txt”结尾的文件名。匹配该模式的文件被认为返回的是真(true),接着将其交给下一个谓词进行解决。

find 会遍历文件系统,将找到的文件名交给谓词测试。如果谓词返回真,就通过。如果返回假,则不再持续往下进行,会接着解决下一个文件名。

谓词 -print 很简略。它总是返回真,同时会将文件名打印到规范输入,因而,能在谓词序列中通过测试而达到这一步的文件都会输入其名称。如果不写,默认会带有这个谓词。

-exec 就有点怪异了。达到这一步的文件名都会变成接下来要执行的命令的一部分。剩下始终到 \; 的这部分就是命令,其中的 {} 会被替换成已查找到的文件名。因而,在下面的例子中,如果 find 在./txt/jazz 子目录中找到名为 1.txt 的文件,那么要执行的命令就会是:

mv ./txt/jazz/1.txt  /txts

所有匹配指定模式的文件都会执行命令。如果找到的文件数量泛滥,那么命令的执行次数天然也不会少。

2、晋升已找到文件的处理速度

依照下面的例子,find 命令会为每个名字符合要求的文件执行命令,然而当文件过多时命令天然会很慢,那么咱们如何进步速度呢?

xargs 命令从规范输出中接管以空白字符分隔(指定 -0 时除外)的文件名,而后对尽可能多的文件(稍微少于零碎的 ARG_MAX 值,参见 15.13 节)执行指定命令。因为调用其余命令会带来不小的开销,因而应用 xargs 能够显著晋升操作速度,因为它可能尽量减少命令的调用次数,而不是每个文件都调用。如下所示:

find . -name '*.txt' -print  | xargs  mv '{}'  /txts;

3、查找文件时不辨别大小写

有些 TXT 文件的扩展名是 .TXT,而不是 .txt。查找时该如何兼顾两者?

用 -iname 谓词(如果应用的 find 版本反对)执行不辨别大小写的搜寻。例如:

find . -iname '*.txt' -print  | xargs  mv '{}'  /txts;

4、按日期查找文件

几个月前,有人给你发了一张 JPEG 图片,你接管后就保留了起来,但当初记不清放哪了。怎样才能找到这张图片呢?

应用 find 命令的 -mtime 谓词来查看文件的最初批改日期。例如:

find . -name '*.jpg' -mtime +90 -print

-mtime 谓词承受一个参数,用于指定要搜寻的时间段。90 代表 90 天。在数字前应用加号(+90)表明要搜寻的文件是在 90 天前批改的。应用减号(-90)表明文件是在 90 天以内批改的。如果既没减号,也没加号,则表明正好就是 90 天。

find 还能够应用逻辑运算符 AND、OR、NOT,如果晓得文件批改工夫至多在一周(7 天)前,但不超过 14 天,那么就能够像上面这样将两个谓词联合起来。如下所示:

find . -mtime +7 -a -mtime -14 -print

5、按类型查找文件

你正在查找名称中带有单词“java”的目录。先尝试了以下命令。

find . -name '*java*' -print

找到的文件太多了,其中还包含文件系统中所有的 Java 源代码文件。应用 -type 谓词只抉择目录。如下:

find . -type d -name '*java*' -print

同样,咱们能够应用 -type f 指定指查找文件。

咱们将 -type d 放在后面,而后是 -name ‘java‘。两者的程序并不影响最终后果,但将 -type d 放在谓词列表的最后面能稍微进步搜寻效率:对于碰到的每个文件,先测试其是否为目录,如果是,才测试名称是否合乎模式。目录的数量比文件要少一些。因而,这种测试程序使得大部分文件不必再进一步比拟名称了。

6、按内容查找文件

你之前写了一份重要的函件,并将其保留为以 .txt 为扩展名的文本文件,但当初想不起文件名的其余部分了。除此之外,惟一记得的就是函件内容中用到过单词“portend”。那么该如何查找已知局部内容的文件呢?

如果文件就在当前目录下,能够应用简略的 grep 命令。

grep -i portend *.txt

如果还没找到,咱们换用一个更齐备的解决方案:find 命令。应用其 -exec 选项对满足谓词的文件执行命令。你能够按下列形式应用 grep 或其余实用工具:

find . -name '*.txt' -exec grep -Hi portend '{}' \;

或者也能够应用 xargs

find . -name '*.txt' | xargs grep -Hi portend

Shell 文本解析 awk

1、保留局部输入

你须要用某种办法保留局部输入,抛弃其余输入。比方咱们日常线上日志,咱们可能会输入很多属性,然而真正能用来解决理论问题的,大多是咱们输入的文字信息。以下代码会打印出所有输出行的第一个字段:

awk '{print $1}' myinput.file

字段之间以空白字符分隔。实用工具 awk 从命令行上指定的文件中读取数据,如果没有指定文件,则从规范输出读取。$1 代表每行以空格宰割后的第一列。

除了下面的写法,咱们还能够通过管道传入:

cat myinput.file | awk '{print $1}'

awk 的用法多变。最简略的用法就是从输出中打印出所选的一个或多个字段。字段之间以空白字符分隔(也能够用 -F 选项指定分隔字符),编号从 1 开始。字段 $0 代表 整个输出行

2、保留局部输出行

你只想保留局部输出行,例如第一个和最初一个字段。举例来说,你心愿 ls 只列出文件名和权限,不须要 ls -l 所提供的其余信息。惋惜的是,ls 并没有相应的选项可能依照这种形式限度输入。能够通过管道将 ls 的输入传给 awk,并从中挑选出你须要的字段,如下:

$ ls -l | awk '{print $1, $NF}'
total 151130
-rw-r--r-- add.1
drwxr-xr-x art
drwxr-xr-x bin
-rw-r--r-- BuddyIcon.png
drwxr-xr-x CDs
drwxr-xr-x downloads
drwxr-sr-x eclipse
...
$

如果咱们用 ls -l 命令的输入。其模式如下所示:

drwxr-xr-x 2 username group 176 2026-10-28 20:09 bin

对于 awk 而言,解析这种输入大海捞针(在 awk 中,默认的字段分隔符为空白字符)。

在输入文件名时,咱们用了点小技巧。在 awk 中,各种字段是用美元符号和字段编号来援用的(如 $1、$2、$3),而且 awk 还有一个内建变量 NF,其中保留着以后行中的字段总数,**$NF** 总是援用最初一个字段。(例如,ls 的输入行共有 8 个字段,因而变量 NF 的值就是 8,$NF 指向的就是输出行中的第 8 个字段,在这个例子中就是文件名)。

留神:读取 awk 变量时不须要应用 $(这一点和 bash 变量不同)。NF 自身就是一个无效的变量援用。在其之前加上 $ 就将其含意从“以后行的字段总数”改成了“以后行的最初一个字段”。

3、颠倒每行的单词

如果想依照逆序输入输出行中的单词。通过下列脚本:

$ awk '{> for (i=NF; i>0; i--) {
> printf "%s", $i;
> }> printf "\n"
> }' <filename>

字符 > 不必你输出,shell 会输入该字符来揭示你还没有敲完命令(shell 在查找能配对的单引号)。因为 awk 程序位于单引号中,因而 bash shell 容许咱们输出多行代码,同时应用 > 作为辅助提示符,直到咱们给出与先前匹配的完结单引号。思考到可读性,咱们在程序中退出了空白字符,不过也齐全能够写成一行。

$ awk '{for (i=NF; i>0; i--) {printf"%s ", $i;} printf"\n"}' <filename>

awk 语言的 for 循环语法和 C 语言中的十分类似。咱们用 for 循环从最初一个字段开始倒着解决到第一个字段,同时输入每个字段的内容。

4、汇总数字列表

如果你须要汇总数字列表,其中有些数字并未呈现在行中。用 awk 先过滤出待汇总的字段,而后再做汇总。这里咱们要对 ls -l 命令输入的文件大小进行汇总。如下:

ls -l | awk '{sum += $5}; END {print sum}'

咱们要汇总 ls -l 输入的第 5 个字段。ls -l 的输入如下所示:

-rw-r--r--. 1 root root  37 12 月  23 21:44 2.text                                           
-rwxr--r--. 1 root root 110 12 月  10 02:20 ifTest.sh

各个字段别离为:权限、链接、所有者、所属组、大小(以字节为单位)、最初一次批改日期、最初一次批改工夫,以及文件名。咱们只对文件大小感兴趣,因而在 awk 程序中用 $5 来援用该字段。咱们在花括号({})里搁置了两段 awk 代码,留神,awk 程序中能够有多个代码段(或代码块)。前有 关键词 END 的代码块仅在程序其余局部实现后运行一次。

本文由 传智教育博学谷 教研团队公布。

如果本文对您有帮忙,欢送 关注 点赞 ;如果您有任何倡议也可 留言评论 私信,您的反对是我保持创作的能源。

转载请注明出处!

退出移动版