0

我想要一个 bash 脚本来实现 bash 命令行本身的一些功能:即命令历史记录和 vi 风格的命令编辑。

该脚本将永远循环(直到 crtl/d)并在终端中读取用户的输入,将每一行视为一个命令。这些命令实际上是一组我已经编写的 shell 脚本,旨在支持照片工作流程。在这种解释环境中应该可以使用相同的编辑和调用功能。

在此脚本中具有 bash 命令历史记录和命令编辑功能将是非常可取的。

4

1 回答 1

0

也在寻找一种在脚本中模拟命令历史的方法,在网上找不到太多关于它的内容,所以我自己构建了一个简单的。不完全符合您的要求,但可能会给您或其他任何人一些参考。

它实际上只是一个大函数,除了处理类似提示的行为并从按 Enter 键返回屏幕上的字符串外,什么都不做。它允许浏览指定的历史文件,同时保存新的输入,返回。下面没有实现自动缩进或移动标记。我认为该脚本需要带有算术外壳的 bash 版本 4,但更改为较旧的语法并且 bash 3 应该可以工作。它还没有完全测试。

将其用作:

./scriptname.sh /optional/path/to/history_file

剧本

#!/bin/bash
# vim: ts=4:

function getInput() {

    local hist_file="${1:-.script_hist}";
    local result="";
    local escape_char=$(printf "\u1b")
    local tab=$(echo -e "\t");
    local backspace=$(cat << eof
0000000 005177
0000002
eof
);

    local curr_hist=0;
    local curr_cmd="";

    function browseHistory() {

        ! test -s "$hist_file" && return 1;

        local max_hist="$(cat "$hist_file" | wc -l || echo 0)";
    
        curr_hist=$((curr_hist + "$1"));

        (( curr_hist > max_hist )) && curr_hist=$max_hist;
        
        if (( curr_hist <= 0 )); then
            curr_hist=0;
            return 1;
        fi

        result="$(sed -n "$((max_hist - curr_hist + 1))p" < "$hist_file")";
    
        return 0;
    }


    ifs=$IFS;

    while true; do

        # empty IFS, read one char
        IFS= read -rsn1 input

        if [[ $input == $escape_char ]]; then
            # read two more chars, this is for non alphanumeric input
            read -rsn2 input
        fi


        # check special case for backspace or tab first
        # then move onto arrow keys or anything else in case
        if [[ $(echo "$input" | od) = "$backspace" ]]; then
        
            # delete last character of current on screen string
            result=${result%?};

        elif [ "$input" = "$tab" ]; then

            # replace with function call for autofill or something
            # it's unused but added in case it would be useful later on
            continue;

        else

            case $input in
                '[A')
                    ! browseHistory '1' && result=$curr_cmd;
                    ;;
                '[B')
                    ! browseHistory '-1' && result=$curr_cmd;
                    ;;
                '[D') continue ;; # left, does nothing right now
                '[C') continue ;; # right, this is still left to do
                *) 
                    # matches enter and returns on screen string
                    [[ "$input" == "" ]] && break;

                    result+=$input
                    ;;
            esac
        fi

        # store current command, for going back after browsing history
        (( curr_hist == 0 )) && curr_cmd="$result";
        
        echo -en "\r\033[K";
        echo -en "${result}"

    done

    IFS=$ifs;

    test -n "$result" && echo "$result" >> "$hist_file";

    return 0;
}

getInput $1
于 2020-09-03T13:55:21.790 回答