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

后端 未结 4 819
囚心锁ツ
囚心锁ツ 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条回答
  • In lieu of pidfiles, as long as your script has a uniquely identifiable name you can do something like this:

      #!/bin/bash
    COMMAND=$0
    # exit if I am already running
    RUNNING=`ps --no-headers -C${COMMAND} | wc -l`
    if [ ${RUNNING} -gt 1 ]; then
      echo "Previous ${COMMAND} is still running."
      exit 1
    fi
    ... rest of script ...
    
    0 讨论(0)
  • 2021-01-03 21:29

    Assume your test commands are stored in a file called "example". That is, using same commands than in previous answer:

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

    the command:

    $ (echo 'PS1=; PROMPT_COMMAND="echo -n =====; echo"'; cat example2 ) | bash -i
    

    produces:

    =====
    x=3
    =====
    echo "$x"
    3
    =====
    y=$(($x+1))
    =====
    echo "$y"
    4
    =====
    
    =====
    
    =====
    while [ "$y" -gt "0" ]
    > do
    >     echo $y
    >     y=$(($y-1))
    > done
    4
    3
    2
    1
    =====
    exit
    

    if you are interested also in the intermediate results of a loop, the command:

    $ ( echo 'trap '"'"'echo; echo command: $BASH_COMMAND; echo answer:'"'"' DEBUG'; cat example ) | bash
    

    results in:

    command: x=3
    answer:
    
    command: echo "$x"
    answer:
    3
    
    command: y=$(($x+1))
    answer:
    
    command: echo "$y"
    answer:
    4
    
    command: [ "$y" -gt "0" ]
    answer:
    
    command: echo $y
    answer:
    4
    
    command: y=$(($y-1))
    answer:
    
    command: [ "$y" -gt "0" ]
    answer:
    
    command: echo $y
    answer:
    3
    
    command: y=$(($y-1))
    answer:
    
    command: [ "$y" -gt "0" ]
    answer:
    
    command: echo $y
    answer:
    2
    
    command: y=$(($y-1))
    answer:
    
    command: [ "$y" -gt "0" ]
    answer:
    
    command: echo $y
    answer:
    1
    
    command: y=$(($y-1))
    answer:
    
    command: [ "$y" -gt "0" ]
    answer:
    

    Addendum 1

    It is not difficult to change the previous results to some other format. By example, this small perl script:

    $ cat formatter.pl 
    #!/usr/bin/perl
    #
    
    $state=4; # 0: answer, 1: first line command, 2: more command, 4: unknown
    
    while(<>) {
    #  print $state;
    
      if( /^===COMMAND===/ ) {
        print "===\n";
        $state=1;
        next;
      }
    
      if( $state == 1 ) {
        print;
        $state=2;
        next;
      }
    
      if( $state == 2 && /^>+ (.*)/ ) {
        print "$1\n";
        next;
      }
    
      if( $state == 2 ) {
        print "---\n";
        $state=0;
        redo;
      }
    
      if( $state == 0 ) {
        print;
        next;
      }
    }
    

    when used in command:

    ( echo 'PS1="===COMMAND===\n"'; cat example ) | bash -i 2>&1 | ./formatter.pl

    gives this result:

    ===
    x=3
    ===
    echo "$x"
    ---
    3
    ===
    y=$(($x+1))
    ===
    echo "$y"
    ---
    4
    ===
    
    ===
    
    ===
    while [ "$y" -gt "0" ]
    do
        echo $y
        y=$(($y-1))
    done
    ---
    4
    3
    2
    1
    ===
    exit
    
    0 讨论(0)
  • 2021-01-03 21:35
    bash -n -c "$command_text"
    

    ...will determine whether your $command_text is a syntactically valid script without actually executing it.


    Note that there's a huge breadth of space between "syntactically valid" and "correct". Consider adopting something like http://shellcheck.net/ if you want to properly parse the language.

    0 讨论(0)
  • 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 '<<<BEGIN>>>'; ${___internal_variable_1___}; printf '<<<END>>>'; declare -p" 2>&1)
            # Separate the environment description from the command output
            y="${out#*<<<END>>>}"
            out="${out%%<<<END>>>*}"
            out="${out#*<<<BEGIN>>>}"
    
            # 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
    ```
    
    0 讨论(0)
提交回复
热议问题