8

In my bash script, I use find to get the folder names by wildcard:

for i in $(find ${directory} -mindepth 1 -type d -name ${wildcard});
do
  stuff=doStuff ${i}
done

doStuff() {
  echo ${1}
  return ${1}'/hello';
}

The problem is that when I do this, I get the following error (lets say that ${i} correspond to 'home/me/my_directory'):

line 111: '/home/me/my_directory': is a directory. 

(line 111 is the line with 'doStuff')

I've tried doing this, to no avail:

for i in $(find ${directory} -mindepth 1 -type d -name ${wildcard});
do
  stuff=doStuff "${i}"
done

Apparently it is because the program is trying to execute the directory, but I just want it as a string that I can manipulate.

3
  • 1
    Hmmm....According to the bash man page....The return builtin command in bash is used to used to return an EXIT code (integer) not any type of string parameter.
    – mdpc
    May 22, 2014 at 19:30
  • You could also try find "${directory}" -mindepth 1 -type d -name "${wildcard}" -print0 | xargs -0 doStuff
    – Daenyth
    May 22, 2014 at 21:40
  • googlers: another great source for getting this error message is //a wrong bash line comment instead of #a correct bash line comment
    – Frank N
    Jan 18, 2017 at 9:35

3 Answers 3

10

Quotes and command substitution are your issues here.

The specific issue you're running into is because the shell is trying to run a command called /home/me/my_directory with the environment variable stuff=doStuff.
What you really want (if I'm interpreting correctly) is to run doStuff with the value of $i as an argument, assigning the output to the variable stuff. The way to do this is to wrap your command in $(). For example:

stuff="$(doStuff "$i")"

Notice How I also put quotes around everything. This is to prevent the shell from word splitting things you don't want it to word split (it'll turn a single argument of /foo/bar baz into /foo/bar and baz).

Also the output of your function is what gets used as the return value, not return.

As you should be using more quotes, you should also add them to everything else. Here is a complete version of your script:

doStuff() {
  echo "${1}" >&2
  printf '%s/hello' "$1";
}

IFS=$'\n'
for i in $(find "${directory}" -mindepth 1 -type d -name "${wildcard}");
do
  stuff="$(doStuff "${i}")"
done

You have to put the function definition before you try and use it. Shell isn't parsed like compiled programs, where it goes through the file several times. It's top-down.
I've also modified doStuff to send the echo to STDERR where it will be shown on the terminal, and then the printf sends to STDOUT where it will be captured into the variable stuff.

Note that this will still have an issue if any of your directories contains a newline. However this is a limitation of the shell. The only character a file path cannot contain is a NULL char (\0). However bash and other shells cannot store a NULL char in a string (not all, but many. I know zsh can), so you can't use it as a delimiter.

1
  • Just wanted to add that if you have space before/after =, it messes up. e.g. MY_VAR='/some/path' is valid but MY_VAR= '/some/path' or MY_VAR ='/some/path' is not Feb 21, 2019 at 8:49
4

That is a good effort but it shows that you are trying to write bash as if it were a programming language, when it is actually a simple scripting language. The problem with your stuff=doStuff arg construction is that to bash it does not mean what you think it means. To bash, stuff=doStuff is a variable assignment, and arg is a command. I suggest you read the following two links:

A working version of something similar to what you have would be

doStuff () {
    echo "$1/hello"
}

stuff=$(doStuff "$i")
1

The line giving the error does not do what you think it does:

It first sets the environment variable stuff to the string "doStuff", and then tries to execute ${i} as a command. And since ${i} is a directory, that fails with the given error message.

You must log in to answer this question.

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