首页IT科技别催了在学了表情包(别催了,别催了,这篇文章我一次性把Shell的内容说完)

别催了在学了表情包(别催了,别催了,这篇文章我一次性把Shell的内容说完)

时间2025-09-10 10:50:14分类IT科技浏览5068
导读:Shell 搜索与匹配 1、在文件中查找字符串...

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 的代码块仅在程序其他部分完成后运行一次            。

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

如果本文对您有帮助                      ,欢迎关注和点赞;如果您有任何建议也可留言评论或私信            ,您的支持是我坚持创作的动力         。

转载请注明出处!

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
日本的vps(【稳定低延迟】日本VPS机房ping测试报告) web学生管理系统课程设计(VUE3.0+Antdv+Asp.net WebApi开发学生信息管理系统(二))