find命令与grep命令,find命令用法及参数和grep

  find命令与grep命令,find命令用法及参数和grep

  两个更有用的命令和正则表达式

  在开始学习新的Shell编程知识之前,我们先来看看两个更有用的命令。虽然这两个命令不是Shell的一部分,但是它们经常在Shell编程中使用。然后我们将看看正则表达式。

  命令查找

  我们先来看看find命令。这个命令对于我们查找文件非常有用,但是对于新的Linux用户来说很难使用它。在某些程序中,是因为它带来的选项、测试、动作类型参数,一个参数的执行结果会影响后面的参数。

  在我们进入这些选项和参数之前,让我们看一个非常简单的例子。假设我们的机器上有一个文件愿望。当我们这样做时,我们必须以root身份运行,这可以确保我们可以搜索整个机器:

  # find/-name愿望-打印

  /usr/bin/wish

  #

  我们可以想象,他会打印出搜索结果。很简单吧?

  但是他的运行需要一定的时间,因为他同时也会在网络上搜索Windows机器上的磁盘。Linux机器将挂载Windows机器的大型文件系统。他还会同时搜索那些位置,尽管我们知道我们要找的文件位于Linux机器上。

  这就是第一种选择的来源。如果我们指定-mount选项,我们可以告诉find命令不要搜索挂载的目录。

  # find/-mount -name愿望-打印

  /usr/bin/wish

  #

  所以我们仍然可以搜索这个文件,但是这次我们没有搜索挂载的文件系统。

  find命令的完整语法如下:

  查找[路径][选项][测试][操作]

  路径是一个非常简单的部分:我们可以使用绝对路径,比如/bin,或者相对路径,比如.如果需要,我们还可以指定多个路径,比如find /var /home。

  一些主要选项如下:

  -depth在查看目录本身之前搜索目录的内容。

  -follow跟随符号链接。

  -max depth N搜索目录时最多搜索N级。

  -mount(或-xdev)不搜索其他文件系统。

  以下是一些测试选项。我们可以为find命令指定大量测试,每个测试将返回true或false。当find命令工作时,它将检查按顺序找到的文件,并在这个文件上按顺序执行它们定义的测试。如果测试返回false,find命令将停止当前正在检查的文件,并继续以下操作。我们在下表中只列出了一些最常用的测试,我们可以通过查看手册页来使用这些测试。

  -N天前-a时间访问的文件

  -mtime n天前修改的文件

  -name pattern是除路径之外与指定类型匹配的文件名。为了确保将指定的类型传递给find命令,而不是由Shell立即赋值,必须用引号将指定的类型括起来。

  -较新的其他文件与其他文件相比是一个新文件。

  -type C C文件,其中C可以指定一个类型。最常用的类型是D表示目录,F表示普通文件。对于其他文件类型,我们可以查看手册页。

  -由userusername指定的用户拥有的文件。

  我们也可以使用运算符来测试组合。大多有两种格式:短格式和长格式。

  !-与-不测试相反

  所有的测试都必须是真的。

  -其中一个o-or测试是真的。

  我们可以使用括号来强制改变测试和操作符的顺序。因为这些对于Shell有特殊的意义,所以我们也需要用反斜杠来整体指代它们。另外,如果我们为文件名指定一个匹配类型,我们也必须用引号将它们括起来,这样就可以防止它们被Shell扩展,并且可以直接将它们传递给find命令。所以如果我们想写这样一个测试,我们需要找到更接近X文件或者以一个范围开始的文件。

  /(-较新的X -o -name "_*" /)

  现在,我们将尝试在当前目录中查找最近修改日期比while2更近的文件。我们可以使用以下命令:

  $ find。-更新的while2 -print。/elif3。/words.txt。/words2.txt。/_陷阱

  $

  我们上面使用的命令看起来不错,但是我们也搜索了当前的目录文件,这不是我们预期的。我们只对常规文件感兴趣,所以我们可以添加另一个测试类型f:

  $ find。-较新的while2型f -print。/elif3。/words.txt。/words2.txt。/_陷阱

  $

  工作原理:

  这些命令是如何工作的?我们指定find命令应该在当前目录中搜索。),但是我们要找的是一个比while2 (-newerhil2)新的文件,如果测试已经通过,我们还需要测试这个文件是否是一个常规文件(-type -f)。最后,我们使用之前使用的操作-print,只是为了验证我们找到的文件。

  我们在下面查找的文件要么以下划线开头,要么比while2文件新,但它也必须是常规文件。这个例子可以告诉我们如何测试组合:

  $ find。/(-name " _ * "-or-newer while 2/)-键入f -print。/elif3。/words.txt。/words2.txt。/_break。/_如果。/_set。/_shift。/_陷阱。/_unset。/_直到

  $

  这时候我们就可以看出,这并不是一件困难的事情,不是吗?我们必须对括号进行转义,这样它就不会受到Shell的保护,并且用引号将*括起来,这样它就可以直接传递给find命令。

  既然我们可以可靠地搜索文件,那么让我们来看看在搜索指定文件时可以进行的一些协作。我们想再次强调,我们在这里列出的只是一些最常用的选项,我们可以查看手册页以获得完整的选项集。

  -exec command执行命令。这是我们最常见的动作。

  -ok命令类似于-exec,只是它会在执行要执行的命令之前提示用户确认该命令。

  -print打印出文件名。

  -ls使用ls命令列出当前文件。

  -exec和-ok命令将使用同一行的参数子序列作为其参数的一部分,直到遇到终止符/为止;序列。对于-exec和-ok,字符串{}是一种特殊的类型,将被当前文件的绝对路径替换。

  这个解释可能不太好理解,但一个例子或许能很好的说明。

  下面举个简单的例子:

  $ find。-更新的while2型f-exec ls-l { }/;

  -rwxr-xr-x 1里克里克275二月八日17:07。/elif3

  -rwxr-xr-x 1瑞克瑞克336 2月8日16:52。/words.txt

  -rwxr-xr-x 1里克里克1274年2月8日16时52分。/words2.txt

  -rwxr-xr-x 1瑞克瑞克504二月八日18:43。/_陷阱

  $

  正如我们现在看到的,find命令非常有用。只需要一些简单的练习就可以很好地使用这个命令。但是,这样的练习可能要花一些钱,所以我们应该用find命令做一些实验。

  Grep命令

  我们将看到的第二个非常有用的命令是grep命令,这是一个不常见的名称。它是通用正则表达式解析器的缩写。我们使用find命令在我们的系统中找到所需的文件,但是我们必须使用grep命令在文件中找到指定的字符串。事实上,最常见的方法是当我们使用find命令时,将grep作为命令传递给-exec。

  grep命令可以接受选项、匹配模式和我们正在寻找的文件:

  grep[选项]模式[文件]

  如果没有指定文件名,他将搜索标准输入。

  先说grep命令的主要选项。我们这里只列出一些主要选项,从手册中可以得到更详细的描述。

  -c打印出匹配行的总数,而不是打印出匹配行。

  -E开放扩展表达式

  -h禁止在输出行前面加上找到匹配内容的文件名。

  -我忽略案件。

  -l列出带有匹配行的文件名,而不是输出实际的匹配行。

  -v转换匹配类型以选择不匹配的行,而不是匹配的行。

  比如下面的例子:

  $ grep in words.txt

  我们三个什么时候再见面。打雷,闪电,还是下雨?

  我来了,格雷马尔金!

  $ grep -c in words.txt words2.txt

  words.txt:2

  words2.txt:14

  $ grep -c -v in words.txt words2.txt

  words.txt:9

  word 2 . txt:16

  $

  工作原理:

  在第一个示例中,没有指定任何选项。grep命令只是在words.txt文件中查找字符串,并打印出匹配的行。这里不打印文件名,因为我们在这里只使用一个文件。

  在第二个示例中,在两个不同的框中打印出匹配行的总数。在这种情况下,打印出文件名。

  在最后一个示例中,我们使用-v选项来转换搜索条件,并打印出两个文件中不匹配的总行数。

  正则表达式

  我们可以看到,grep命令的基本用法相对容易掌握。现在让我们看看基本的正则表达式,它将允许我们做一些更复杂的匹配。正如我们前面提到的,正则表达式用在Linux或其他开源语言中。我们可以在vi中或编写Perl脚本时使用它们。

  在使用正则表达式的过程中,一些字符以不同的方式处理。一些最常见的用法如下:

  在行首

  行尾的$号

  任何单个字符

  []方括号包含字母的范围,任何一个都可以匹配,比如a-e的字母范围,或者我们可以用“for antonym”。

  如果我们想把它们作为普通字符使用,我们必须在这些字符前面加上/所以如果我们想找一个$字符,就要用/$来找。

  以下是一些可用在方括号中的有用的特殊匹配:

  [:alnum:]字母数字字符

  [:alpha:]字母

  [:ascii:] ASCII字符

  [:blank:]空格或制表符

  [:cntrl:] ASCII代码控制字符

  [:digit:]数字

  [:graph:]不受控制的非空格字符

  [:lower:]小写字母

  [:print:]可打印字符

  [:punch:]标点符号

  [:space:]空白字符,包括垂直制表符

  [:upper:]大写字符

  [:xdigital:]十六进制数字

  此外,如果同时使用-E选项指定扩展匹配,正则表达式后可能会有一些字符与其他控件匹配类型组合在一起。如果我们只是想把它们当作普通字符来使用,就必须在它们前面加上转义符/。

  ?可选匹配,但最多匹配一次。

  *必须匹配0个或更多项目。

  必须匹配一个或多个项目。

  {n}必须匹配n次。

  {n,}必须匹配n次或更多次。

  {n,m}匹配范围从n次到m次,包括m次。

  这些内容看起来很复杂,但如果我们一步步走下去,就会发现,其实这些内容并没有我们第一次看到时那么复杂。掌握正则表达式最简单的方法就是尝试一些例子:

  如果我们想找到以字符E结尾的行,我们可以使用下面的命令:

  $ grep e$ words2.txt

  你不是,致命的视觉,明智的吗

  我还能看见你,以可触摸的形式

  大自然似乎死了,邪恶的梦想滥用

  $

  正如我们所看到的,这个命令将搜索以e结尾的匹配行。

  现在假设我们想找到以字母a结尾的单词,为了达到这个目的,我们在方括号中使用特殊匹配。在这种情况下,我们将使用[[:blank:]],它将测试空格或制表符:

  $ grep a[[:blank:]] words2.txt

  我面前的这把匕首,

  思想的匕首,虚假的创造,

  像幽灵一样移动。你这坚定的大地,

  $

  现在假设我们想找到一个以th开头的三个字母的单词。在这种情况下,我们需要同时使用[[:space:]]来确定单词的结尾和。要匹配另一个字母:

  $ grep Th。[[:space:]] words2.txt

  把手朝向我的手?来吧,让我抓住你。

  窗帘睡着了;巫术庆祝

  你的石头在谈论我的下落,

  $

  最后,我们将使用扩展的grep命令来查找长度为10个字符的小写字母单词。这里,我们将指定匹配A到Z的字符范围,同时指定10个重复的字符:

  $ grep -E [a-z]/{10/} words2.txt

  从热压迫的大脑开始?

  我要用的就是这样一种工具。

  窗帘睡着了;巫术庆祝

  你的石头在谈论我的下落,

  $

  在这里,我们只触及正则表达式的一些相对重要的部分。像Linux中的大多数其他内容一样,将会有许多文档来帮助我们找到更详细的内容,但是学习正则表达式的最好方法是对它们进行实验。

  命令执行:

  当我们编写脚本时,我们经常需要在Shell脚本中获得命令执行的结果来使用它。也就是说,我们需要执行一个命令,并将这个命令的输出结果放在一个变量中。此时,我们可以使用在前面的set命令示例中引入的$(command)语法。这也是一种比较古老的格式,最常用的格式是“命令”格式。

  所有新脚本都应该使用$(.),可以用来避免在反引号命令中使用$,`,/导致的一些相当复杂的转换规则。如果反引号用于.`结构,我们需要用/来转义。这些相对模糊的字符会混淆程序,有时甚至一些有经验的程序也要做一些实验才能使反引号命令中的引号正确。

  (command)命令的结果只是一个简单命令的输出。这里我们要注意,这不是这个命令的返回状态,而是输出字符串。如下例所示:

  #!/bin/sh

  echo当前目录是$PWD

  echo目前的用户是$(谁)

  出口0

  因为当前目录是一个Shell环境变量,所以第一行不需要使用这个命令执行结构。但是,如果我们希望who命令的执行结果在这个脚本中可见,我们必须使用这个命令结构。

  如果我们想把它们的结果放在一个变量中,我们可以像往常一样把它们赋给一个变量:

  谁在那里=$(谁)

  echo $whoisthere

  将命令的执行结果放在脚本变量中的能力非常强大,因为在脚本中使用当前命令并获得它们的输出很容易。如果你发现你试图在标准输出上转换一个标准命令的输出结果的参数集,并把它们作为一个程序的参数,你会发现xargs命令会帮助你完成这一切。您可以查看手册页,了解更深入、更详细的内容。

  有时候,有一个问题是,我们想要调用的命令会在我们想要的文本出现之前输出一些空白,或者比我们想要的更多的内容。在这种情况下,我们可以使用前面提到的set命令。

  算术扩展

  我们已经使用了expr命令,它允许处理简单的算术命令,但是它的执行相当慢,因为在处理expr命令时需要调用一个新的Shell。

  一个新的更好的选择是$ ((.))扩展。通过将我们需要的表达式放在括号中,这样就可以用$((.)),我们可以更有效地执行简单的算术。

  比如下面这个例子:

  #!/bin/sh

  x=0

  while[" $ x "-ne 10];做

  回显$x

  x=$(($x 1))

  完成的

  出口0

  参数扩展

  我们在前面已经看到了参数分配和扩展的最简单形式,我们是这样写的:

  foo=弗雷德

  echo $foo

  当我们想在一个变量的末尾添加另一个字符时,就会出现问题。假设我们想写一个简短的脚本来处理名为1_tmp和2_tmp的文件,我们可以尝试下面的脚本:

  #!/bin/sh

  因为我在1 2

  做

  my_secret_process $i_tmp

  完成的

  但是在每个周期中,我们得到以下信息:

  我的秘密进程:参数太少

  怎么了?

  问题是Shell会尝试用它的变量值替换变量$i_tmp,但是这个变量并不存在。而且Shell并不认为这是一个错误,只是用一个null值来替换,所以没有参数传递给my_secret_process。为了保护作为变量一部分的$i的扩展名,我们需要将I放在一对花括号中:

  #!/bin/sh

  因为我在1 2

  做

  my_secret_process ${i}_tmp

  完成的

  然后,在第一个循环中,I的值将被替换为${i},从而给出一个实际的文件名。所以我们已经用字符串替换了参数的值。

  我们可以在外壳中进行许多替换。通常,这种方法会为参数处理问题提供一个优雅的解决方案。

  一些常用的表格如下:

  ${parm:-default}如果参数为空,则将其设置为默认值。

  ${#parm}给出参数的长度。

  ${parm%word}从末尾开始,删除与word匹配的最小部分,并返回其余部分。

  ${parm%%word}从末尾开始,删除与word匹配的最长部分,返回其余部分。

  ${parm#word}从开头开始,删除与word匹配的最小部分,返回其余部分。

  ${parm##word}从开头开始,去掉与word匹配的最长部分,返回其余部分。

  这些替换对于我们想要处理的字符串非常有用。最后四个可以用来删除字符串的部分内容,这对处理文件名和路径更有用。如下面的一些例子所示:

  #!/bin/sh

  unset foo

  echo ${foo:-bar}

  foo=fud

  echo ${foo:-bar}

  foo=/usr/bin/X11/startx

  echo ${foo#*/}

  echo ${foo##*/}

  bar=/usr/local/etc/local/networks

  echo ${bar%local*}

  echo ${bar%%local*}

  出口0

  如果我们运行这个脚本,我们将得到以下输出:

  酒吧

  飞悠达

  usr/bin/X11/startx

  进入图形界面

  /usr/local/等等

  /usr

  工作原理:

  第一句话${foo:-bar}将把foo的值指定为bar,因为在执行这条语句时没有为foo指定任何值。foo的值将保持不变,直到他遇到unset语句。

  这里有一些需要我们注意的事情:

  ${foo:=bar}将设置变量$foo。这个字符串操作符将检测foo是否存在并且不为空。如果不为空,则返回其值,反之,则将foo的值设置为bar,返回替换后的结果值。

  ${foo:Bar}会打印foo: bar,如果foo不存在或者设置为null,就会退出命令。

  最后,如果foo存在并且不为空,${foo: bar}将返回bar。

  {foo#*/}语句匹配并且只删除左边的内容(这里我们要记住*匹配0个或者更多的字符)。{foo##*/}匹配并删除尽可能多的内容,所以他删除了最右边的/和他前面的所有字符。

  {bar%local*}语句匹配右侧的字符,直到第一次出现local,而{bar%%local*}匹配右侧尽可能多的字符,直到第一次出现local。

  因为Unix和Linux都非常依赖过滤的概念,所以我们经常不得不手动重定向操作的执行结果。假设我们想使用cjpeg命令将GIF文件转换成jpeg文件:

  $ CJ peg image . gif image.jpg

  也许有时我们在大量文件上这样做。这时候我们怎么自动重定向?我们很容易做到这一点:

  #!/bin/sh

  对于*中的图像。可交换的图像格式

  做

  cjpeg $image ${image%%gif}jpg

  完成的

  这个脚本可以将当前目录下的每个GIF文件转换成JPEG文件。

find命令与grep命令,find命令用法及参数和grep