2

The following code is meant to look for subdirectories in ~/Downloads. I run it with . ./script.sh. It will find them even when the user submits an incomplete name.

#!/usr/bin/bash

echo -e "\nGive me the name of a nonexitent directory and I will look for it in ~/Downloads?\n"

read word

TOGOs=("$(find ~/Downloads -maxdepth 1 -iname "*$word*" -type d -execdir echo {} + | sed 's;./;/home/smith/Downloads/;g')"
"")
for togo in ${TOGOs[@]}
do
  if [[ $togo != "" ]]; then
    echo $togo
    export togo && cd $togo && return 0
  else
    echo "Haven't found it in ~/Downloads ." && cd ~/Downloads #This line does not work
  fi
done

The if part works as expected - when I give it a name/part of the name of a subdirectory of ~/Downloads/, but the else part of the block never gets executed when I give it a nonexisting directory. I can get the else part executed when I get rid of the loop, like so:

#!/usr/bin/bash

echo -e "\nGive me the name of a nonexitent directory and I will look for it in ~/Downloads?\n"

read word
TOGO=$(find ~/Downloads -maxdepth 1 -iname "*$word*" -type d -execdir echo {} + | sed 's;./;/home/smith/Downloads/;g')

if [[ $TOGO != "" ]]; then
  echo $TOGO
  export TOGO 
  cd $TOGO && return 0
else

  echo "Haven't found it in ~/Downloads." && cd ~/Downloads
fi

Why is it so the else arm gets executed when I get rid of the loop? How might I get my code executed while preserving the loop?

1 Answer 1

2

${TOGOs[@]} means to take the elements of the array, break them into separate words at whitespace (assuming the default IFS), and interpret each word as a glob pattern. It's the same as $var for a scalar variable, except that it lists all the elements in turn.

If there's an empty element in the array, the splitting step turns it into a list of 0 words, so it's effectively removed.

As always: use double quotes around variable and command substitutions unless you know that you need to leave them out. To list the elements of an array, the correct syntax is "${TOGOs[@]}". The array access “wins” over the double quotes: each element of the array is placed into a separate word.

Using the correct syntax for array listing won't help you, though, because the way you construct the array doesn't make sense. You're putting the whole output of find into a single array element. You can't parse the output of find that way: there's no way to distinguish between a newline that's part of a file name and a newline that find uses to distinguish file names.

Instead of parsing the output of find, do the processing in bash, or use find -exec …. Bash has recursive globbing (enabled by shopt -s globstar) if you need it, but since you're using find -maxdepth 1 you actually don't need it, and find isn't really doing anything useful.

shopt -s dotglob nullglob
TOGOs=(~/Downloads/*$word*/)
if [[ ${#TOGOs[@]} -eq 0 ]]; then
  echo "Haven't found it in ~/Downloads."
fi
for togo in "${TOGOs[@]}"; do
  …
done
2
  • 1
    Thanks, your code works fine. Simply placing double quotes around ${TOGOs[@]} also solved the issue in my code. Would you decode # in if [[ ${#TOGOs[@]} -eq 0 ]]? P.S. "" is meant to be the second element of the array TOGOs.
    – John Smith
    Commented Jan 28, 2022 at 18:58
  • 2
    @JohnSmith ${#TOGOs[@]} is the length of the array. This checks whether the array is empty. Commented Jan 28, 2022 at 21:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .