在循环bash脚本中模拟命令历史记录和编辑

时间:2018-03-21 01:31:45

标签: bash input editing input-history

我希望有一个bash脚本来实现bash命令行本身的一些功能:即命令历史记录和vi样式的命令编辑。

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

非常需要在此脚本中使用bash命令历史记录和命令编辑功能。

1 个答案:

答案 0 :(得分:0)

在寻找一种也可以在脚本中模拟命令历史记录的方法时,由于在网上找不到太多内容,因此自己创建了一个简单的脚本。可能不完全是您的要求,但可能会给您或其他人一些参考。

这实际上只是一个大功能,除了处理诸如行为之类的提示并按Enter返回屏幕上的字符串外,别无其他。它允许浏览指定的历史文件,同时保存新的输入以返回。自动缩进或移动标记不在下面实现。我认为该脚本需要带有算术shell的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