5

I'm trying to iterate over each file in a directory. Here's my code so far.

while read inputline
do
  input="$inputline"
  echo "you entered $input";

if [ -d "${input}" ]
  then
    echo "Good Job, it's a directory!"

    for d in $input
      do
        echo "This is $d in directory."
      done
   exit

my output is always just one line

this is $input directory.

why isn't this code working? what am I doing wrong?

Cool. When I echo it prints out

$input/file

Why does it do that? Shouldn't it just print out the file without the directory prefix?

Mawnster
  • 673
  • 3
  • 11
  • 18

3 Answers3

7
for d in "$input"/*
pixelbeat
  • 30,615
  • 9
  • 51
  • 60
  • why add double quote " around $input, is there a good reason doing this? I found out " for d in $input/* " also works. Thanks. – Lion Lai Mar 23 '17 at 04:00
  • 1
    If $input has spaces etc. you need to quote. – pixelbeat Mar 23 '17 at 22:24
  • 1
    @LionLai See also [When to wrap quotes around a shell variable](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) – tripleee Dec 20 '21 at 07:42
2

If you want to simplify it somewhat and get rid of the directory check, you could just write it to work on files and directories, perhaps something like:

read inputline
ls "$inputline" | while read f; do
    echo Found "$f"
done
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
-1

You would think that looping over files would be easy, right? But this is full of pitfalls in bash.

Using globs is the WORST. Trust me, don't do it

for x in *; do          # <--- bad for many reasons
    echo the file name is $x
done

Using find is better, for instance.

for x in `find . -maxdepth 1 -type f`; do  # <-- assume no filename has spaces
    echo the file name is $x
done

find has a lot of options to filter results by name, by date, by owner... whatever. It is very powerful.

However using a for-find FAILS if the filename contains spaces. To fix that use...

while read x; do
    echo the file name is $x
done < <(find . -maxdepth 1 -type f)

Or if you don't like that weird done syntax, instead you can use:

result=`find . -maxdepth 1 -type f`
while read x; do
    echo the file name is $x
done <<< $result

However, what if the filename contains a linefeed?! Can that happen? Yes it can happen, but it is extremely rare. So if you are PARANOID you can do:

while read -r -d '' x; do
    echo the file name is $x
done < <(find . -maxdepth 1 -type f -print0)

In my opinion the extra mess is not worth it, so I don't recommend it. People who put linefeeds in filenames deserve to feel pain.

John Henckel
  • 10,274
  • 3
  • 79
  • 79
  • I think you need grounding when advising against globs. A very reputable shell script linter (31k stars on GitHub) says exactly the opposite, see [here](https://github.com/koalaman/shellcheck/wiki/SC2045). What's wrong with globs when used with `nullglob` flag? Regarding `find` - it's similar to using `ls` in for loops (which is a common anti-pattern, see the link above). I believe using `find` is an anti-pattern, too. It may fail for many reasons (i.e. outputting errors instead of file names), it may output errors in the middle of file list when it iterates over inaccessible paths etc. – Denis P Mar 03 '23 at 18:35