is there a way to check if a bash script is complete or not?

后端 未结 4 818
囚心锁ツ
囚心锁ツ 2021-01-03 21:08

I\'m trying to implement a REPL (read-eval-print loop) in bash. If such a thing already exists, please ignore the following and answer this question with a pointer to it.

4条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2021-01-03 21:51

    The following scripts should generate the Markdown output you expect.

    eval "set -n; $x" is used to verify if the command is complete, by checking for syntax errors in the command. Only a command that has no syntax errors will be considered complete, executed, and shown in the output Markdown.

    Please note that the input script that is to be processed is executed in a sub-shell and therefore will not interfere with the processing script itself (i.e. the input script can use the same variable names as the processing script and cannot change the values of variables in the processing script). The only exception are the special variables called ___internal__variable___.

    There are two approaches to how to achieve that, which I present below. In Version 1, whenever a new complete command is processed, all the statements before it are executed to create a "context" for the command. This effectively runs the input script multiple times.

    In Version 2, the environment of the sub-shell is stored in a variable after each complete command is executed. Then, before the next command is executed, the previous environment is restored in the sub-shell.

    Version 1

    #!/bin/bash
    
    x=""  # Current
    y=""  # Context
    while IFS= read -r line  # Keep indentation
    do
        [ -z "$line" ] && continue  # Skip empty lines
    
        x=$x$'\n'$line  # Build a complete command
        # Check current command for syntax errors
        if (eval "set -n; $x" 2> /dev/null)
        then
            # Run the input script up to the current command
            # Run context first and ignore the output
            ___internal_variable___="$x"
            out=$(eval "$y" &>/dev/null; eval "$___internal_variable___")
            # Generate command markdown
            echo "=================="
            echo
            echo "\`\`\`bash$x"
            echo "\`\`\`"
            echo
            # Generate output markdown
            if [ -n "$out" ]
            then
                echo "Output:"
                echo
                echo "\`\`\`"
                echo "$out"
                echo "\`\`\`"
                echo
            fi
            y=$y$'\n'$line  # Build context
            x=""  # Clear command
        fi
    done < input.sh
    

    Version 2

    #!/bin/bash
    
    x=""  # Current command
    y="true"  # Saved environment
    while IFS= read -r line  # Keep indentation
    do
        [ -z "$line" ] && continue  # Skip empty lines
    
        x=$x$'\n'$line  # Build a complete command
        # Check current command for syntax errors
        if (eval "set -n; $x" 2> /dev/null)
        then
            # Run the current command in the previously saved environment
            # Then store the output of the command as well as the new environment
            ___internal_variable_1___="$x"  # The current command
            ___internal_variable_2___="$y"  # Previously saved environment
            out=$(bash -c "${___internal_variable_2___}; printf '<<>>'; ${___internal_variable_1___}; printf '<<>>'; declare -p" 2>&1)
            # Separate the environment description from the command output
            y="${out#*<<>>}"
            out="${out%%<<>>*}"
            out="${out#*<<>>}"
    
            # Generate command markdown
            echo "=================="
            echo
            echo "\`\`\`bash$x"
            echo "\`\`\`"
            echo
            # Generate output markdown
            if [ -n "$out" ]
            then
                echo "Output:"
                echo
                echo "\`\`\`"
                echo "$out"
                echo "\`\`\`"
                echo
            fi
            x=""  # Clear command
        fi
    done < input.sh
    

    Example

    For input script input.sh:

    x=10
    echo "$x"
    y=$(($x+1))
    echo "$y"
    
    
    while [ "$y" -gt "0" ]
    do
        echo $y
        y=$(($y-1))
    done
    

    The output will be:

    ==================
    
    ```bash
    x=10
    ```
    
    ==================
    
    ```bash
    echo "$x"
    ```
    
    Output:
    
    ```
    10
    ```
    
    ==================
    
    ```bash
    y=$(($x+1))
    ```
    
    ==================
    
    ```bash
    echo "$y"
    ```
    
    Output:
    
    ```
    11
    ```
    
    ==================
    
    ```bash
    while [ "$y" -gt "0" ]
    do
        echo $y
        y=$(($y-1))
    done
    ```
    
    Output:
    
    ```
    11
    10
    9
    8
    7
    6
    5
    4
    3
    2
    1
    ```
    

提交回复
热议问题