Bash 复合条件,带有通配符和文件存在性检查

发布于 2024-09-15 20:08:45 字数 808 浏览 1 评论 0原文

我已经掌握了 Bash 复合条件的基础知识,并阅读了几种不同的方法来检查通配符文件是否存在,但是这个方法让我困惑,所以我想我应该寻求帮助......

我需要: 1.) 检查是否存在某些匹配模式的文件 和 2.) 检查不同文件中的文本是否存在。

我知道有很多方法可以做到这一点,但我真的不知道如何确定它们的优先级(如果您有这些知识,我也有兴趣阅读相关内容)。

首先想到的是使用 find 来查找 #1 并使用 grep 来查找 #2

一样

if [ `grep -q "OUTPUT FILE AT STEP 1000" ../log/minimize.log` ] \
      && [ `find -name "jobscript_minim\*cmd\*o\*"` ]; then
   echo "Both passed! (1)"
fi

所以像That

if `grep -q "OUTPUT FILE AT STEP 1000" ../log/minimize.log` ;then
   echo "Text passed!"
fi
if `find -name "jobscript_minim\*cmd\*o\*"` ;then
   echo "File passed!"
fi

失败了,但奇怪的是:两者都通过了......

我读了一些书,看到人们在谈论多重问题文件名与 if 语句中的通配符匹配。对此最好的解决方案是什么? (在回答我的问题时,我假设您在此过程中也尝试过这个问题)

有什么想法/解决方案/建议吗?

I've mastered the basics of Bash compound conditionals and have read a few different ways to check for file existence of a wildcard file, but this one is eluding me, so I figured I'd ask for help...

I need to:
1.) Check if some file matching a pattern exists
AND
2.) Check that text in a different file exists.

I know there's lots of ways to do this, but I don't really have the knowledge to prioritize them (if you have that knowledge I'd be interested in reading about that as well).

First things that came to mind is to use find for #1 and grep for #2

So something like

if [ `grep -q "OUTPUT FILE AT STEP 1000" ../log/minimize.log` ] \
      && [ `find -name "jobscript_minim\*cmd\*o\*"` ]; then
   echo "Both passed! (1)"
fi

That fails, though curiously:

if `grep -q "OUTPUT FILE AT STEP 1000" ../log/minimize.log` ;then
   echo "Text passed!"
fi
if `find -name "jobscript_minim\*cmd\*o\*"` ;then
   echo "File passed!"
fi

both pass...

I've done a bit of reading and have seen people talking about the problem of multiple filenames matching wildcards within an if statement. What's the best solution to this? (in answer my question, I'd assumed you take a crack at that question, as well, in the process)

Any ideas/solutions/suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最后的乘客 2024-09-22 20:08:46

让我们首先解决尝试失败的原因:

if [ `grep -q …` ]; 

这会在反引号之间运行 grep 命令,并将输出插入条件命令内。由于 grep -q 不会产生任何输出,就好像您编写了 if [ ];

条件应该测试 grep 的返回代码code>,没有任何关于其输出的信息。因此,它应该简单地写为

if grep -q …;

find 命令返回 0(即 true),即使它什么也没找到,所以这种技术不起作用。有效的方法是通过收集其输出与空字符串进行比较来测试其输出是否为空:(

if [ "$(find …)" != "" ];

测试是 if [ -n "$(find …)" ]。)

等效 这里有两件事:

  • 我使用了$(…)而不是反引号。它们是等效的,只是反引号需要在其中使用奇怪的引号(特别是当您尝试嵌套它们时),而 $(...) 简单且可靠。只需使用 $(...) 并忘记反引号(除非您需要在双引号内写入 \`)。

  • $(…) 周围有双引号。这真的很重要。如果没有引号,shell 会将 find 命令的输出分解为单词。如果 find 打印两行 dir/filedir/otherfile,我们需要 if [ "dir/file dir/ otherfile" = "" ]; 被执行,而不是 if [ dir/file dir/otherfile = "" ]; 这是一个语法错误。这是 shell 编程的一般规则:始终在变量或命令替换两边加上双引号。 (变量替换为 $foo${foo};命令替换为 $(command)。)


现在让我们看看您的要求。

  1. 检查是否存在与模式匹配的某些文件

    如果您要在当前目录或其下的任何目录中递归查找文件,那么 find -name "PATTERN" 是正确的。然而,如果目录树变得很大,效率就会很低,因为当我们只关心一个匹配项时,它会花费大量时间来打印所有匹配项。一个简单的优化是通过管道输入 head -n 1 来仅保留第一行;一旦发现 head 不再对它所说的内容感兴趣,find 将停止搜索。

    if [ "$(find -name "jobscript_minimcmdo" | head -n 1)" != "" ];

    (请注意,双引号已经防止通配符扩展。)

    如果您只在当前目录中查找文件,假设您有 GNU find(Linux、Cygwin 和 Gnuwin32 上就是这种情况),一个简单的解决方案是告诉它不要递归到比当前目录更深的位置。

    if [ "$(find -maxdepth 1 -name "jobscript_minim*cmd*o*")" != "" ];

    还有其他更便携的解决方案,但编写起来更复杂。

  2. 检查不同文件中的文本是否存在。

    您已经获得了正确的 grep 命令。请注意,如果您想搜索文字字符串,您应该使用 grep -F ;如果您正在寻找正则表达式,grep -E 的语法比普通 grep 更清晰。

把它们放在一起:

if grep -q -F "OUTPUT FILE AT STEP 1000" ../log/minimize.log &&
   [ "$(find -name "jobscript_minim*cmd*o*")" != "" ]; then
  echo "Both passed! (1)"
fi

Let's tackle why your attempt failed first:

if [ `grep -q …` ]; 

This runs the grep command between backticks, and interpolates the output inside the conditional command. Since grep -q doesn't produce any output, it's as if you wrote if [ ];

The conditional is supposed to test the return code of grep, not anything about its output. Therefore it should be simply written as

if grep -q …;

The find command returns 0 (i.e. true) even if it finds nothing, so this technique won't work. What will work is testing whether its output is empty, by collecting its output any comparing it to the empty string:

if [ "$(find …)" != "" ];

(An equivalent test is if [ -n "$(find …)" ].)

Notice two things here:

  • I used $(…) rather than backticks. They're equivalent, except that backticks require strange quoting inside them (especially if you try to nest them), whereas $(…) is simple and reliable. Just use $(…) and forget about backticks (except that you need to write \` inside double quotes).

  • There are double quotes around $(…). This is really important. Without the quotes, the shell would break the output of the find command into words. If find prints, say, two lines dir/file and dir/otherfile, we want if [ "dir/file dir/otherfile" = "" ]; to be executed, not if [ dir/file dir/otherfile = "" ]; which is a syntax error. This is a general rule of shell programming: always put double quotes around a variable or command substitution. (A variable substitution is $foo or ${foo}; a command substitution is $(command).)


Now let's see your requirements.

  1. Check if some file matching a pattern exists

    If you're looking for files in the current directory or in any directory below it recursively, then find -name "PATTERN" is right. However, if the directory tree can get large, it's inefficient, because it can spend a lot of time printing all the matches when we only care about one. An easy optimization is to only retain the first line by piping into head -n 1; find will stop searching once it realizes that head is no longer interested in what it has to say.

    if [ "$(find -name "jobscript_minimcmdo" | head -n 1)" != "" ];

    (Note that the double quotes already protect the wildcards from expansion.)

    If you're only looking for files in the current directory, assuming you have GNU find (which is the case on Linux, Cygwin and Gnuwin32), a simple solution is to tell it not to recurse deeper than the current directory.

    if [ "$(find -maxdepth 1 -name "jobscript_minim*cmd*o*")" != "" ];

    There are other solutions that are more portable, but they're more complicated to write.

  2. Check that text in a different file exists.

    You've already got a correct grep command. Note that if you want to search for a literal string, you should use grep -F; if you're looking for a regexp, grep -E has a saner syntax than plain grep.

Putting it all together:

if grep -q -F "OUTPUT FILE AT STEP 1000" ../log/minimize.log &&
   [ "$(find -name "jobscript_minim*cmd*o*")" != "" ]; then
  echo "Both passed! (1)"
fi
晒暮凉 2024-09-22 20:08:46

重击4

shopt -s globstar
files=$(echo **/jobscript_minim*cmd*o*)
if grep -q "pattern" file && [[ ! -z $files ]];then echo "passed"; fi

bash 4

shopt -s globstar
files=$(echo **/jobscript_minim*cmd*o*)
if grep -q "pattern" file && [[ ! -z $files ]];then echo "passed"; fi
北城半夏 2024-09-22 20:08:46
for i in filename*; do FOUND=$i;break;done
if [ $FOUND == 'filename*' ]; then
echo “No files found matching wildcard.”
else
echo “Files found matching wildcard.”
fi
for i in filename*; do FOUND=$i;break;done
if [ $FOUND == 'filename*' ]; then
echo “No files found matching wildcard.”
else
echo “Files found matching wildcard.”
fi
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文