Reading each line as argument in bash

2020-02-16 02:02发布

问题:

I am trying to learn bashing. I am trying to run ./test testcase the file in test case has the arguments 9 11 22 13, 32 35 32 16 on the next line and so on My program takes 4 arguments. Right now if testcase has one line of arguments i.e 3 5 6 7 it works fine, but when there is more than 2 it can't run the program properly. I know I need a while loop to read each line of the file, but I am stuck here. If someone could help me it would be greatly appreciated. Thank you in advance. I've asked this question here before, I don't know if everyone is too busy or don't know how. Thanks again

your_path=../test/test
test_path=../../public/test    
file_input="$1"
#while read -r line; do
#done < "$file_input"

# Read contents in the file
contents=$(< "$file_input")
# Display output of the test file
"$test_path" $contents > correctanswer 2>&1
# Display output of your file
"$your_path" $contents > youranswer 2>&1
diff the solutions
diff correctanswer youranswer > /dev/null 2>&1
if [ $? -eq 0 ]
then
  echo "The two outputs were exactly the same "
else
  echo "$divider"
  echo "The two outputs were different "
  diff youranswer correctanswer
  echo "Do you wish to see the ouputs side-by-side?"
      select yn in "Yes" "No"; do
      case $yn in
         Yes ) echo "LEFT: Your Output   RIGHT: Solution Output"
               sleep 1
               vimdiff youranswer correctanswer; break;;
         No ) exit;;
      esac
      done
fi

回答1:

I would normally use a construct similar to the following (applying to your example). Note that the only change your need to make is the cat $file | (while read l; do and then done) that closes the cycle. The body of the cycle is exactly your code with exception that contents is replaced with l (line read). Parenthesis around the cycle actually launch another instance of the shell where while is executed and its stdin is set to be fed from cat.

your_path=../test/test
test_path=../../public/test    
file_input="$1"

exec 3< $file_input
while read -u 3 l; do
# do your per-test diffs and outputs here
# variable $l will contain one line of $file_input
    # Display output of the test file
    "$test_path" $l > correctanswer 2>&1
    # Display output of your file
    "$your_path" $l > youranswer 2>&1
    # diff the solutions
    diff correctanswer youranswer > /dev/null 2>&1
    if [ $? -eq 0 ]
    then
      echo "The two outputs were exactly the same "
    else
      echo "$divider"
      echo "The two outputs were different "
      diff youranswer correctanswer
      echo "Do you wish to see the ouputs side-by-side?"
          select yn in "Yes" "No"; do
          case $yn in
             Yes ) echo "LEFT: Your Output   RIGHT: Solution Output"
                   sleep 1
                   vimdiff youranswer correctanswer; break;;
             No ) exit;;
          esac
          done
    fi

done


回答2:

I think you can do it like this:

your_path=../test/test
test_path=../../public/test    
file_input="$1"
while read -r -u 3 contents
do
    # Display output of the test file
    "$test_path" $contents > correctanswer 2>&1
    # Display output of your file
    "$your_path" $contents > youranswer 2>&1
    # diff the solutions
    diff correctanswer youranswer > /dev/null 2>&1
    if [ $? -eq 0 ]
    then
        echo "The two outputs were exactly the same "
    else
        echo "$divider"
        echo "The two outputs were different "
        diff youranswer correctanswer
        echo "Do you wish to see the ouputs side-by-side?"
        select yn in "Yes" "No"; do
            case $yn in
                Yes)  echo "LEFT: Your Output   RIGHT: Solution Output"
                      sleep 1
                      vimdiff youranswer correctanswer
                      break;;
                 No)  exit;;
            esac
        done
    fi
done 3< "$file_input"

Modified so that the read command reads on file descriptor 3, leaving standard input available for the select, etc.


I don't know what you're doing that makes it fail. I created two programs, test1 and test2. Initially, they were identical:

printf "%s\n" "$@"

Then I changed test2 so it contained:

printf "%s\n" "1$@2"

With this data in input.file:

9 11 22 13
2 35 32 16

I ran the script botched.sh:

$ bash -x botched.sh input.file
+ '[' -f /etc/bashrc ']'
+ . /etc/bashrc
++ '[' -z '' ']'
++ return
+ alias 'r=fc -e -'
+ your_path=./test1
+ test_path=./test2
+ file_input=input.file
+ read -r -u 3 contents
+ ./test2 9 11 22 13
+ ./test1 9 11 22 13
+ diff correctanswer youranswer
+ '[' 0 -eq 0 ']'
+ echo 'The two outputs were exactly the same '
The two outputs were exactly the same 
+ read -r -u 3 contents
+ ./test2 2 35 32 16
+ ./test1 2 35 32 16
+ diff correctanswer youranswer
+ '[' 0 -eq 0 ']'
+ echo 'The two outputs were exactly the same '
The two outputs were exactly the same 
+ read -r -u 3 contents
$

The outputs were recognized as identical, as you'd expect. Then with the modified test2, I reran it:

$ bash -x botched.sh input.file
+ '[' -f /etc/bashrc ']'
+ . /etc/bashrc
++ '[' -z '' ']'
++ return
+ alias 'r=fc -e -'
+ your_path=./test1
+ test_path=./test2
+ file_input=input.file
+ read -r -u 3 contents
+ ./test2 9 11 22 13
+ ./test1 9 11 22 13
+ diff correctanswer youranswer
+ '[' 1 -eq 0 ']'
+ echo ''

+ echo 'The two outputs were different '
The two outputs were different 
+ diff youranswer correctanswer
1c1
< 9
---
> 19
4c4
< 13
---
> 132
+ echo 'Do you wish to see the ouputs side-by-side?'
Do you wish to see the ouputs side-by-side?
+ select yn in '"Yes"' '"No"'
1) Yes
2) No
#? 1
+ case $yn in
+ echo 'LEFT: Your Output   RIGHT: Solution Output'
LEFT: Your Output   RIGHT: Solution Output
+ sleep 1
+ vimdiff youranswer correctanswer
2 files to edit
+ break
+ read -r -u 3 contents
+ ./test2 2 35 32 16
+ ./test1 2 35 32 16
+ diff correctanswer youranswer
+ '[' 1 -eq 0 ']'
+ echo ''

+ echo 'The two outputs were different '
The two outputs were different 
+ diff youranswer correctanswer
1c1
< 2
---
> 12
4c4
< 16
---
> 162
+ echo 'Do you wish to see the ouputs side-by-side?'
Do you wish to see the ouputs side-by-side?
+ select yn in '"Yes"' '"No"'
1) Yes
2) No
#? 2
+ case $yn in
+ exit
$

The vimdiff program did run and show the differences.

Note the basic debugging technique — running with bash -x. It shows that the read is taking a single line of input with four numbers, and then those are passed to the surrogate programs test1 and test2; then it takes a second line, and passes those to the surrogates programs.

Actual test code:

your_path=./test1 #../test/test
test_path=./test2 #../../public/test    
file_input="$1"
while read -r -u 3 contents
do
    # Display output of the test file
    "$test_path" $contents > correctanswer 2>&1
    # Display output of your file
    "$your_path" $contents > youranswer 2>&1
    # diff the solutions
    diff correctanswer youranswer > /dev/null 2>&1
    if [ $? -eq 0 ]
    then
        echo "The two outputs were exactly the same "
    else
        echo "$divider"
        echo "The two outputs were different "
        diff youranswer correctanswer
        echo "Do you wish to see the ouputs side-by-side?"
        select yn in "Yes" "No"; do
            case $yn in
                Yes)  echo "LEFT: Your Output   RIGHT: Solution Output"
                      sleep 1
                      vimdiff youranswer correctanswer
                      break;;
                 No)  exit;;
            esac
        done
    fi
done 3< "$file_input"

Tested with Bash 3.2.51 on Mac OS X 10.9.2.

If it doesn't work for you, then you need to set up surrogate test programs, similar to mine, and show what you get from running bash -x on your script. You should also identify the platform you're running on and the version of Bash you are running.