【问题标题】:String comparison precedence in BashBash中的字符串比较优先级
【发布时间】:2010-09-21 07:13:28
【问题描述】:

以下示例将比较目录中的所有文件以输入字符串($string)并返回匹配的文件名。这不是非常优雅和有效的方式来实现这一点。出于速度目的,我修改了for 条件以仅与以 $string 的第一个单词开头的文件进行比较。

此脚本的问题如下 - 我的目录中有两个文件:

Foo Bar.txt
Foo Bar Foo.txt

我将它们与字符串 "Foo Bar 09.20.2010" 进行比较。这将返回该目录中的两个文件,因为两个文件都匹配。但我只需要以最精确的方式返回与字符串匹配的文件 - 在我们的示例中,它应该是 Foo Bar.txt

另外,如果你有更好的想法来解决这个问题,请发表你的想法,因为我还不是很精通脚本,我相信有更好甚至更简单的方法来做到这一点。

#!/bin/bash
string="Foo Bar 09.20.2010"

for file in /path/to/directory/$(echo "$string" | awk '{print $1}')*; do

    filename="${file##*/}"
    filename="${filename%.*}"


    if [[ $(echo "$string" | grep -i "^$filename") ]]; then
        result="$file"
        echo $result    
    fi

done

这是我想要实现的细分。目录中的两个文件与两个字符串匹配,括号中的正确/不正确表示结果是否符合我的预期/想要的。

目录中的2个文件(为了匹配而去掉了扩展名):

Foo Bar.txt
Foo Bar Foo.txt

要与 2 个字符串进行比较:

Foo Bar Random Additional Text
Foo Bar Foo Random Additional Text

结果:

compare "Foo Bar"(.txt) against Foo Bar Random Additional Text -> Match (Correct)
compare "Foo Bar"(.txt) against Foo Bar Foo Random Additional Text -> Match (Incorrect)

compare "Foo Bar Foo"(.txt) against Foo Bar Random Additional Text -> NOT Match (Correct)
compare "Foo Bar Foo"(.txt) against Foo Bar Foo Random Additional Text -> Match (Correct)

谢谢大家的回答。

【问题讨论】:

  • @Andrew - 所以如果有一个完全匹配你想要只是,但是当没有完全匹配时,'部分'匹配会做吗?
  • @martin Clayton 是的,如果没有找到完全匹配,则首选部分匹配。
  • 好吧,看来我提供的例子不是最好的。让我再试一次 - 字符串 Foo Bar 09.20.2010 应该匹配文件 Foo Bar.txt。并且文件Foo Bar Foo 09.20.2010 应该匹配文件Foo Bar Foo.txt。我希望这现在更有意义......
  • 09.20.2010 是什么?
  • @John Kugelman 我添加了09.20.2010 表示该字符串可能包含不在文件名中的文本。这主要是我没有使用ls -1 的原因。我不使用ls -1 的另一个原因是因为我将文件名与字符串进行比较,反之亦然。这就是我首先剥离文件扩展名的原因。

标签: bash compare operator-precedence


【解决方案1】:

如果我错了,请纠正我,但您的脚本似乎相当于:

ls /path/to/directory/"$string"*

如果你只想要一个文件名,你可以使用head。由于ls 按字母顺序列出文件,您将按字母顺序获得第一个文件。

(请注意,当ls 的输出通过管道传输到另一个程序时,它每行打印一个文件名,这比正常的基于列的输出更容易处理。)

ls /path/to/directory/"$string"* | head -1

对于 shortest 匹配尝试类似以下的操作,它使用 awksort -ncut 的尴尬组合来将行从最短到最长排序,然后打印第一个。

ls /path/to/directory/"$string"* |
    awk '{print length($0) "\t" $0}' | sort -n | head -1 | cut -f 2-

【讨论】:

    【解决方案2】:

    您的很多echoawk 电话都是多余的。要获取所有以匹配开头的文件,您可以简单地评估“$string”*。

    例如两者都有

    echo "$string"*
    

    ls "$string"*
    

    将生成您的列表。 (在管道中, echo 将使它们以空格分隔,而 ls 将使它们以换行符分隔)。

    下一步要意识到,鉴于此,正如您所定义的那样,“最精确匹配”的额外约束等同于最短匹配文件名。

    在 bash 中找到一组字符串中最短的字符串(我自己更喜欢 perl,但让我们坚持在 bash 中执行它的约束):

    for fn in "/path/to/$string"*; do
      echo $(echo $fn | wc -c) "$fn"
    done | sort -n | head -1 | cut -f2- -d' '
    

    for 循环遍历扩展的文件名。回声将名称的长度添加到名称中。然后,我们将它的整个输出通过管道传输到 sort -nhead -1 以获得最短的名称,然后 cut -f2- -d' ' 去掉它的长度(使用第二个字段作为字段分隔符)。

    Shell 编程的关键是了解您的构建块,以及如何组合它们。通过巧妙地组合排序、头部、尾部和切割,您可以进行许多非常复杂的处理。加上 sed 和 uniq,你已经可以做一些令人印象深刻的事情了。

    话虽这么说,我通常只将 shell 用于“即时”之类的事情——对于我可能想要重复使用的任何东西,而且这很复杂,我更有可能使用perl.

    【讨论】:

    • 事实上,我不介意使用 perl/python/php 或任何其他可以完成工作的语言。我只是发现 bash 更容易使用,即使我添加了很多多余的命令,正如你所说:)
    猜你喜欢
    • 2022-11-21
    • 2013-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 2015-08-30
    • 2012-03-02
    相关资源
    最近更新 更多