Skip to content

Bash sample code: Create a speed test

Jean-Michel Gigault edited this page Dec 30, 2015 · 9 revisions

42FileChecker features a speedtest to compare two programs or two functions together. This test is based on the number of outputted lines, and strictly requires that the two programs works exactly the same way! Here is how it works! (Note: This is not a reliable test ;-) )


Sample code

# This function executes a command and returns the number of ouputted lines
# Returns -1 if the program has failed or terminated in less than $TIMEOUT
function speedtest_exec_command
{
    local COMMAND=$1              # The program to execute
    local TIMEOUT=30              # Duration of execution of the test
    local LOGFILE=.tmp_speedtest  # Temporary file to save outputs
    local TOTAL                   # Value to return

    # Execute, redirect outputs, send it to background and get PID
    ($COMMAND 1>$LOGFILE 2>&1) &
    local PID=$!

    # Wait for the duration and check if the process is still running
    sleep $TIMEOUT
    if [ ! -z "$(ps a | awk '{print $1}' | grep $PID)" ]
    then
        # If yes, stop the process...
        kill $PID
        wait $! 2>/dev/null

        # ...and count the number of lines written in the temporary file
        TOTAL=$(cat $LOGFILE | wc -l | sed 's/[^0-9]*//g')
    else
        # If not, the program has failed running the minimum duration
        TOTAL=-1
    fi

    rm -f $LOGFILE          # Remove the temporary file
    printf "%d" "$TOTAL"    # and print the returned value $TOTAL
}

# This is the main function that takes 3 arguments:
# - The name of the Student's program
# - The name of the original Unix program
# - The arguments to pass to each program on execution
function speedtest
{
    local PROG1=$1
    local PROG2=$2
    local ARGUMENTS=$3

    # Call the 'speedtest_exec_command' with the 1st program
    # and save the result in a variable $RET1
    echo "RUNNING: '$PROG1 $ARGUMENTS'"
    local RET1=`speedtest_exec_command "$PROG1 $ARGUMENTS"`
    echo "Total of lines: $RET1"

    # Call the 'speedtest_exec_command' with the 2nd program
    # and save the result in a variable $RET2
    echo "RUNNING: '$PROG2 $ARGUMENTS'"
    local RET2=`speedtest_exec_command "$PROG2 $ARGUMENTS"`
    echo "Total of lines: $RET2"

    # Calculate the ratio between $RET1 and $RET2 and print it
    local RATIO
    (( RATIO = $RET1 * 100 / $RET2 ))
    echo "\nRatio: $RATIO%"
}

# Call the speedtest (e.g: compare 'ft_ls' and 'ls')
speedtest "ft_ls" "ls" "-1R /"

Tips


Redirect outputs of a command line
declare LOGFILE_STDOUT=.standard_output
declare LOGFILE_STDERR=.standard_error

# Redirect STDOUT and STDERR to distinct files
cat * 1>$LOGFILE_STDOUT 2>$LOGFILE_STDERR

declare LOGFILE=.outputs

# Redirect STDOUT and STDERR to the same file
cat * 1>$LOGFILE 2>&1   # '&1' means 'to where the stream #1 is going'
cat * 2>$LOGFILE 1>&2   # The same result but inverted

cat * 2>&1 1>$LOGFILE   # Invalid order of redirections!

Count the number of lines of a file
declare FILENAME=42FileChecker.sh

cat $FILENAME                               # Read the file
cat $FILENAME | wc -l                       # Display the number of lines
cat $FILENAME | wc -l | sed 's/[^0-9]*//g'  # Keep only the numeric value

# Now save the result in a numeric variable $TOTAL
declare -i TOTAL
TOTAL=$(cat $FILENAME | wc -l | sed 's/[^0-9]*//g')
echo "Number of lines: $TOTAL"

Execute a command and store the result in a variable

You can set the content of a variable as a command to run, by surrounding the command with backticks `, which is the legacy syntax for oldest shells, or by using the command substitution syntax $(...).

declare MY_NAME

MY_NAME=`uname`   # using backticks
MY_NAME=$(uname)  # using command substitution

echo "My name is $MY_NAME"

As you can read on this wiki post Why is $(...) preferred over `...` (backticks)?, you would prefer to use the clearest command substitution syntax, especially with nesting command substitutions like bellow:

declare SEARCH_RESULTS

SEARCH_RESULTS=`grep "\`uname\`" "./libft.h"`
SEARCH_RESULTS=$(grep "$(uname)" "./libft.h")  # clearest way