简体   繁体   中英

how to execute a command using array containing strings with spaces linux

I have been stuck on an issue for a couple of hours now regarding bash shell arrays.

I am using an array of strings created from an inputFile using IFS=$'\\n' as the separator.

Each index of the array may contain multiple words separated by spaces. Each string represents a combination of Options/Arguments that may be used with the execution of another shell script.

I attempt to supply the these strings to the other shell script I wrote using variations of the following syntax:

    # Run the testcases, case by case
    for testcase in `cat $inputFile` ###"${testcases[@]}"
    #for ((i = 0; i < ${#testcases[@]}; i++))
            do
                    #testcase="${testcases[i]}"
                    # Print each case in inputFile
                    echo "${testcase}"

                    # Run the Archive Script, redirect output to /dev/null
                    ./archive.sh "${testcase}" > /dev/null

    # Respond to $# return value

done

You will notice a few different variations I have used to loop through an array (or attempt to directly respond by directly reading into a local variable from the output of cat .

What is most frustrating is that the echo command works, and prints the string with spaces. However, when my script attempts to execute using the string, the first word is read as a whole but following the first space, the script attempts to execute the script a single character at a time. :(

Please help! Here is the output I get when executing the script:

$ test_archive.sh lab6_testcase
Testcases Count: 6
Running cases:
-v dirA
./archive.sh: illegal option --  
./archive.sh: illegal option -- d
./archive.sh: illegal option -- i
./archive.sh: illegal option -- r
./archive.sh: illegal option -- A
dirB
-v -r dirA
./archive.sh: illegal option --  
./archive.sh: illegal option -- -
./archive.sh: illegal option -- r
./archive.sh: illegal option --  
./archive.sh: illegal option -- d
./archive.sh: illegal option -- i
./archive.sh: illegal option -- r
./archive.sh: illegal option -- A
-v dirA dirB
./archive.sh: illegal option --  
./archive.sh: illegal option -- d
./archive.sh: illegal option -- i
./archive.sh: illegal option -- r
./archive.sh: illegal option -- A
./archive.sh: illegal option --  
./archive.sh: illegal option -- d
./archive.sh: illegal option -- i
./archive.sh: illegal option -- r
./archive.sh: illegal option -- B
dirc
-a
./archive.sh: illegal option -- a
TEST SUMMARY
SUCCESSFUL CASES:0
USAGE CASES:0
INVALID ARGUMENT CASES:6
-v dirA
dirB
-v -r dirA
-v dirA dirB
dirc
-a

Here is the Full Script incase I have missed some key line triggering and error:

#!/bin/bash
# CONSTANTS
SUCCESS=0
USAGE_ERROR=1
INVALID_ARGUMENT=2

# Set the Input Field Seperator to '\n'
IFS=$'\n'

# Setup counters
usageErrorCount=0
argumentErrorCount=0
successCount=0

# Check if not enough or too many arguments have bee supplied
if (( $# != 1 ))
then
   echo Usage: $0 filename
   exit $USAGE_ERROR
fi

# Store the File in inputFile
inputFile=$1

# Check if the inputFile exists
if [[ ! -f $inputFile ]]
then
        # Report that the file does not exist
        echo "Exiting: The input file '$inputFile' does not exist!"
        exit $INVALID_ARGUMENT
fi

# Read the lines from the file, and place them in the array
testcases=( `cat $inputFile` )

echo Testcases Count: ${#testcases[@]}


# Inform use of case list
echo Running cases:

# Run the testcases, case by case
for testcase in `cat $inputFile` ###"${testcases[@]}"
#for ((i = 0; i < ${#testcases[@]}; i++))
        do
                #testcase="${testcases[i]}"
                # Print each case in inputFile
                echo "${testcase}"

                # Run the Archive Script, redirect output to /dev/null
                ./archive.sh "${testcase}" > /dev/null

                # Use Switch Statement on ENV Success Var (#?) to:
                        # 1. Increment counters
                        # 2. Add testcase to appropriate array
                case $? in
                        $USAGE_ERROR)           # Add testcase to usage array
                                                                usageCases[$usageErrorCount]="$testcase"
                                                                # Up Usage Count
                                                                usageErrorCount=$((usageErrorCount+1))

                        ;;
                        $INVALID_ARGUMENT)      # Add testcase to argument array
                                                                argumentCases[$argumentErrorCount]="$testcase"
                                                                # Up Argument Count
                                                                argumentErrorCount=$((argumentErrorCount+1))

                        ;;
                        $INVALID_ARGUMENT)      # Add testcase to success array
                                                                successCases[$successCount]="$testcase"
                                                                # Up Success Count
                                                                successCount=$(($successCount+1))
                        ;;
                        esac
        done

# Format the Output
echo "TEST SUMMARY"

# Report Successful Cases
echo "SUCCESSFUL CASES:$successCount"
for testcase in  ${successCases[@]}
        do
                echo $testcase
        done

# Report Ussage Cases
echo "USAGE CASES:$usageErrorCount"
for testcase in  ${usageCases[@]}
        do
                echo $testcase
        done

# Report Successful Cases
echo "INVALID ARGUMENT CASES:$argumentErrorCount"
for testcase in  ${argumentCases[@]}
        do
                echo $testcase
        done

# Exit with Success
exit $SUCCESS

The problem is due to your IFS still being set to $'\\n' so ./archive.sh is reading the whole line as a single argument and trying to make sense of the letters.

Change the IFS back to ' ' and the problem should be gone!

Example:

    # Run the testcases, case by case
    for testcase in "${testcases[@]}"
            do
                    IFS=' '
                    echo "${testcase}"

                    ./archive.sh "${testcase}" > /dev/null

done
 ./archive.sh "${testcase}"

You are calling archive.sh with a single argument (with embedded whitespace). That's what double quotes do. Everything inside double quotes is a single word.

Since the first character of the argument is a dash, archive.sh takes the rest of the argument (including the embedded space) as options, and complain about them not being valid options.

You want to drop the quotes. Demo .

I found a solution to the issue after some digging.

The issue is that the shell executes the command before evaluating/dereferencing the contents of the variable (or array value).

Because of this, eval must be used to force the shell to dereference the variable/array and retrieve the contents before executing the command proceeding the variable/array.

Here is a working version (reading directly from file):

for testcase in `cat $inputFile`
        do
                #testcase="${testcases[i]}"
                # Print each case in inputFile
                echo "  ${testcase}"

                # Run the Archive Script, redirect output to /dev/null
                eval ./lab5.sh ${testcase} > /dev/null

... CONTINUE WITH SCRIPT ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM