1

Recently I made a bash executable file with permissions 722 as I am almost perpetually root. The contents of the file are as follows:

#!/home/nolan/Documents/test/listFiles

[ $# -lt 1 ] && dirFocus = "" || dirFocus = $1
dirSize=$(ls -a $dirFocus | wc -w)

for ((a = 1; a <= $dirSize; a++)) ; do
    i = 1
    for ITEM in $(ls -a $dirFocus); do
        declare -i i
        declare -i a
        if [ $a -eq $i ]; then
            echo "$a : $ITEM"
        fi
        i = $[ $i + 1 ]
    done
done

When run in terminal using:

root @ /home/nolan/Documents/test: bash listFiles
1 : .
2 : ..
3 : apple
4 : dirCheck
5 : ifTest
6 : ifTest.txt
7 : listFiles
8 : myscript
9 : nolan.txt
10 : pointer_to_apple
11 : scriptx.sh
12 : Stuff
13 : weekend
14 : weekend2
15 : weekend3

I receive this outcome as expected. However the second I do:

root @ /home/nolan/Documents/test: ./listFiles
bash: ./listFiles: /home/nolan/Documents/test/listFiles: bad interpreter: Too
many levels of symbolic links

Is the error I get. What exactly is going wrong? I've checked other forums but they don't seem to pertain to my situation.

  • The first sentence of the question contains a falsehood, which is of course the basis of an answer. – JdeBP Aug 10 '18 at 16:54
  • 2
    722 permissions seem wrong. You want to give the world write permission? – glenn jackman Aug 10 '18 at 18:15
9

The first line of a script is the "shebang" line. It tells kernel (program loader) what program to run to interpret the script. Your script tries to run itself to interpret the script, which in turn calls itself to interpret the interpreter, and so on ad infinity.

When you run the script with bash filename, the kernel isn't invoked and bash is used to run the script which works.

Put

#! /bin/bash

to the first line and all will be fine.

BTW, create a user with limited privileges to experiment with the system. As root, you can easily destroy everything beyond repair.

  • 1
    That something continues ad infinitum is not a very good explanation of why it stops immediately with an error. (-: – JdeBP Aug 10 '18 at 16:56
  • @JdeBP "Ad finem" then? – Kusalananda Aug 10 '18 at 17:41
  • It doesn't error out immediately, "Linux allows at most BINPRM_MAX_RECURSION, that is 4, levels of nesting." (git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/… , though that's different in current versions, might be this part: git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/… ). – ilkkachu Aug 10 '18 at 19:57
  • That's immediate as far as the questioner is concerned, who described the behaviour as happening "the second" that xe attempted this. You're also looking at the wrong code in that first. ENOEXEC is not the error in the question. – JdeBP Aug 11 '18 at 0:34
4

choroba explained very well that your issue is with the shell script using itself as interpreter instead of bash.

Here's a suggestion for reimplementation of your script that

  1. Does not contain syntax errors (dirFocus = "" is, for example, a syntax error in that = should have no spaces around it in an assignment).

  2. Deals with filenames that have spaces and newlines (looping over the output of ls disqualifies any such filename, likewise wc -w would get the wrong count if any filename contained spaces).

  3. Uses modern shell syntax ($[ $i + 1 ] is obsolete bash syntax).

  4. Introduces tiny improvement in output (adds (dir) to directory names).

#!/bin/bash

shopt -s dotglob    # make wildcards match hidden names by default
shopt -s nullglob   # make wildcards expand to nothing if no match

count=0
for name in "${1:-.}"/*; do
    count=$(( count + 1 ))
    if [ -d "$name" ]; then
        printf '%d : %s (dir)\n' "$count" "${name##*/}"
    else
        printf '%d : %s\n' "$count" "${name##*/}"
    fi
done

This also does not expand the file list more than once, whereas your code does an ls on the whole directory for each name in the directory.

${1:-.} means "use what's in $1 unless it's empty or unset, otherwise use .".

${name##*/} means "the string in $name, but without the bit up to the last /".

  • Thanks a lot man, this does wonders for me and my future scripts! I was playing around with one part I didn't understand, namely after "${1:-.}" where it's followed by /* ... what purpose does /* serve here? I get that if there's no parameter, then replace with "." but what does "./*" signify? – NolanRudolph Aug 11 '18 at 4:39
  • 1
    @NolanRudolph * is a filename globbing character that matches any string in a filename, so the complete thing, ${1:-.}/* expands to all filenames in the directory given by the first command line argument, or to all filenames in the current directory if no command line argument was given. – Kusalananda Aug 11 '18 at 6:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.