以 bash 反向搜索历史风格搜索文件

发布于 2024-10-31 10:30:27 字数 2696 浏览 5 评论 0原文

我正在尝试编写一个函数,它将以与反向搜索历史记录相同的方式搜索文件。即用户开始输入,提示会更新为第一个匹配项,按下特殊键会轮流显示其他匹配项,按下另一个特殊键会选择当前匹配项。

我为此编写了一个 bash 脚本,但速度非常慢。想知道我是否可以利用其他一些 unix/bash 功能来加快速度。也许使用 awk?

任何想法将不胜感激。

对于此脚本,TAB 循环匹配,ENTER 选择当前匹配,ESC 结束,BACKSPACE 删除当前搜索中的最后一个字符。 (原谅我狡猾的 bash 脚本,我对 bash/unix 比较陌生)

#!/bin/bash


do_search()
{
        #Record the current screen position
        tput sc
        local searchTerm
        local matchNumber=1
        local totalSearchString
        local TAB_CHAR=`echo -e "\t"`

        #print initial prompt
        echo -n "(search) '':"

        #-s: means input will not be echoed to screen, -n1 means that one character will be gotten
        while IFS= read -r -s -n1 char
        do
                #If ENTER
                if [ "$char" == "" ]; then
                        if [ "$match" != "" ]; then
                                eval "$match"
                        fi
                        echo ""
                        return 0

                #If BACKSPACE
                elif [ "$char" == "" ]; then
                        if [ "$totalSearchString" != "" ]; then
                                totalSearchString=${totalSearchString%?}
                        fi

                #If ESCAPE
                elif [ "$char" == "" ]; then
                        tput el1
                        tput rc
                        return 0

                #If TAB
                elif [ "$char" == "$TAB_CHAR" ]; then
                        matchNumber=$(($matchNumber+1))

                #OTHERWISE
                else
                        totalSearchString="$totalSearchString$char"
                fi

                match=""
                if [ "$totalSearchString" != "" ]; then
                        #This builds up a list of grep statements piping into each other for each word in the totalSearchString
                        #e.g. totalSearchString="blah" will output "| grep blah"
                        #e.g. totalSearchString="blah1 blah2" will output "| grep blah1 | grep blah2"
                        local grepStatements=`echo $totalSearchString | sed 's/\([^ ]*\) */| grep \1 /g'`
                        local cdHistorySearchStatement="cat $1 $grepStatements | head -$matchNumber | tail -1"

                        #Get the match
                        match=`eval "$cdHistorySearchStatement"`
                fi

                #clear the current line
                tput el1
                tput rc

                #re-print prompt & match
                echo -n "(search) '$totalSearchString': $match"
        done
  return 0
}

do_search categories.txt

I'm trying to write a function which will search through a file in the same manner that reverse-search-history works. i.e. user starts typing in, prompt is updated with 1st match, hitting a special key rotates through other matches, hitting another special key selects the current match.

I wrote a bash script to this, but it is awfully slow. Was wondering if I could harness some other unix/bash feature to make this fast. Maybe using awk?

Any ideas would be appreciated.

For this script, TAB rotates through matches, ENTER selects the current match, ESC ends, BACKSPACE removes the last character in the current search. (forgive my dodgy bash script, am relatively new to bash/unix)

#!/bin/bash


do_search()
{
        #Record the current screen position
        tput sc
        local searchTerm
        local matchNumber=1
        local totalSearchString
        local TAB_CHAR=`echo -e "\t"`

        #print initial prompt
        echo -n "(search) '':"

        #-s: means input will not be echoed to screen, -n1 means that one character will be gotten
        while IFS= read -r -s -n1 char
        do
                #If ENTER
                if [ "$char" == "" ]; then
                        if [ "$match" != "" ]; then
                                eval "$match"
                        fi
                        echo ""
                        return 0

                #If BACKSPACE
                elif [ "$char" == "" ]; then
                        if [ "$totalSearchString" != "" ]; then
                                totalSearchString=${totalSearchString%?}
                        fi

                #If ESCAPE
                elif [ "$char" == "" ]; then
                        tput el1
                        tput rc
                        return 0

                #If TAB
                elif [ "$char" == "$TAB_CHAR" ]; then
                        matchNumber=$(($matchNumber+1))

                #OTHERWISE
                else
                        totalSearchString="$totalSearchString$char"
                fi

                match=""
                if [ "$totalSearchString" != "" ]; then
                        #This builds up a list of grep statements piping into each other for each word in the totalSearchString
                        #e.g. totalSearchString="blah" will output "| grep blah"
                        #e.g. totalSearchString="blah1 blah2" will output "| grep blah1 | grep blah2"
                        local grepStatements=`echo $totalSearchString | sed 's/\([^ ]*\) */| grep \1 /g'`
                        local cdHistorySearchStatement="cat $1 $grepStatements | head -$matchNumber | tail -1"

                        #Get the match
                        match=`eval "$cdHistorySearchStatement"`
                fi

                #clear the current line
                tput el1
                tput rc

                #re-print prompt & match
                echo -n "(search) '$totalSearchString': $match"
        done
  return 0
}

do_search categories.txt

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

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

发布评论

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

评论(2

流星番茄 2024-11-07 10:30:27

我认为 bash 为此使用了 readline,你为什么不考虑自己使用它呢?抱歉,我对此了解不多,但我认为这可能会有所帮助。

I think bash uses readline for this, why don't you look into using it yourself? I don't know much more about it, sorry, but I thought it might help.

橙幽之幻 2024-11-07 10:30:27

我认为这对于纯 bash 中的交互使用来说不够快(也许使用 complete 内置函数?)。也就是说,您可以尝试简化您正在使用的命令。 来对所有单词使用一个 grep,而不是每个单词一个 grep

  grepStatements=$(echo "$totalSearchString" | sed 's/[ ]\+/|/g')
  cdHistorySearchStatement="grep '$grepStatements' $1 | ..."

您可以通过执行and 代替 head -$matchNumber | tail -1 你可以使用sed -n ${matchNumber}{p;q}

I don't think this can be made fast enough for interactive use in pure bash (maybe using the complete builtin?). That said, you can try simplifying the commands you're using. Instead of one grep per word, you can use one for all of them, by doing

  grepStatements=$(echo "$totalSearchString" | sed 's/[ ]\+/|/g')
  cdHistorySearchStatement="grep '$grepStatements' $1 | ..."

and instead of head -$matchNumber | tail -1 you could use sed -n ${matchNumber}{p;q}.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文