While trying to write a robust bash method that detects whether a variable that consists of multiple lines of text, contains a substring (that may contain spaces) I am experiencing some difficulties. Three functions were written and I wrote some tests for them using the BATS framework. The functions and the accompanying tests on which they fail, are included below.
Method I
lines_contain_string() {
local substring="$1"
shift
local lines=("$@")
local found_substring="NOTFOUND"
for i in "${lines[@]}"; do
if [[ $i == *"$substring"* ]]; then
echo "FOUND"
local found_substring="FOUND"
fi
done
if [ "$found_substring" == "NOTFOUND" ]; then
echo "NOTFOUND"
fi
}
This fails on test:
EXAMPLE_LINES=$(cat <<-END
First line
second line
third line
sometoken
END
)
@test "Substring in first line is found in lines by lines_contain_string." {
contained_substring="First line"
actual_result=$(lines_contain_string "$contained_substring" "\${EXAMPLE_LINES}")
#actual_result=$(lines_contain_string_with_space "$contained_substring" "\${EXAMPLE_LINES}")
EXPECTED_OUTPUT="FOUND"
assert_equal "$actual_result" "$EXPECTED_OUTPUT"
}
So the first method does not catch all substrings.
Method II
And for the second method:
lines_contain_string_with_space() {
local substring="$1"
local lines="$@"
if [ "$lines" == "" ]; then
echo "NOTFOUND"
# shellcheck disable=SC2154
elif [[ "$lines" =~ "$substring" ]]; then
echo "FOUND";
else
echo "NOTFOUND";
fi
}
This seems to return false positive/seems to always return "FOUND", for example, on test:
EXAMPLE_LINES=$(cat <<-END
First line
second line
third line
sometoken
END
)
@test "lines_contain_string returns NOTFOUND on non-existing substring." {
contained_substring="Non-existing-substring"
#actual_result=$(lines_contain_string "$contained_substring" "\${EXAMPLE_LINES}")
actual_result=$(lines_contain_string_with_space "$contained_substring" "\${EXAMPLE_LINES}")
EXPECTED_OUTPUT="NOTFOUND"
assert_equal "$actual_result" "$EXPECTED_OUTPUT"
}
Method III
This method is provided by choroba:
string_in_lines() {
local substring=$1
shift
local lines=$1
if [[ $lines = *"$substring"* ]] ; then
echo FOUND
else
echo NOTFOUND
fi
}
And calling it with test:
@test "Substring in first line is found in lines by lines_contain_string." {
contained_substring="First line"
read -p "EXAMPLE_LINES=$EXAMPLE_LINES"
#actual_result=$(lines_contain_string "$contained_substring" "\${EXAMPLE_LINES}")
#actual_result=$(lines_contain_string_with_space "$contained_substring" "\${EXAMPLE_LINES}")
actual_result=$(string_in_lines "$contained_substring" "\${EXAMPLE_LINES}")
EXPECTED_OUTPUT="FOUND"
assert_equal "$actual_result" "$EXPECTED_OUTPUT"
}
Produces output:
✗ Substring in first line is found in lines by lines_contain_string.
(from function `assert_equal' in file test/no_server_required/preserves_server/../../libs/bats-assert/src/assert_equal.bash, line 40,
in test file test/no_server_required/preserves_server/test_parsing.bats, line 35)
`assert_equal "$actual_result" "$EXPECTED_OUTPUT"' failed
EXAMPLE_LINES=First line
second line
third line
sometoken
-- values do not equal --
expected : FOUND
actual : NOTFOUND
--
Question
How can one echo "FOUND"/"NOTFOUND" in a bash function, based on whether an incoming variable, consisting of multiple lines, contains a substring that may contain spaces? (And test it accordingly.)
Note
Since my tests also fail on the method provided by choroba, which I assume has a large likelihood of being successful, I expect my test functions are not correct. I updated the question accordingly.
The comments by choroba suggested the backslash in: `"${EXAMPLE_LINES}" was incorrect. I tested whether that was incorrect, and it turned out it was incorrect. So the solution was to write the test as:
@test "Substring in first line is found in lines by lines_contain_string." {
contained_substring="First line"
#actual_result=$(lines_contain_string "$contained_substring" "${EXAMPLE_LINES}")
#actual_result=$(lines_contain_string_with_space "$contained_substring" "${EXAMPLE_LINES}")
actual_result=$(string_in_lines "$contained_substring" "${EXAMPLE_LINES}")
EXPECTED_OUTPUT="FOUND"
assert_equal "$actual_result" "$EXPECTED_OUTPUT"
}
@test "lines_contain_string returns NOTFOUND on non-existing substring." {
contained_substring="Non-existing-substring"
#actual_result=$(lines_contain_string "$contained_substring" "${EXAMPLE_LINES}")
#actual_result=$(lines_contain_string_with_space "$contained_substring" "${EXAMPLE_LINES}")
actual_result=$(string_in_lines "$contained_substring" "${EXAMPLE_LINES}")
EXPECTED_OUTPUT="NOTFOUND"
assert_equal "$actual_result" "$EXPECTED_OUTPUT"
}
CodePudding user response:
Use =
with a wildcard pattern.
string_in_lines() {
local substring=$1
shift
local lines=$1
if [[ $lines = *"$substring"* ]] ; then
echo FOUND
else
echo NOTFOUND
fi
}