0

I'm having trouble looping through the files in a directory with bash. I'm quite new to bash and can't seem to figure out the issue that I'm having.

When I loop through the files in a directory that I explicitly mention, there doesn't seem to be a problem. However, when a variables is used to describe the directory to loop through, things don't seem to work out.

The working loop, that finds all the files in ~/Desktop/:

for file in ~/Desktop/*; do
    echo "$file"
done

However, the following doesn't seem to work and only displays ~/Desktop/*,

DEST="~/Desktop/*"    

for file in "$DEST"; do
    echo "$file"
done

Hopefully there's something small that I'm missing here. My main goal is that I'd like to be able to loop over an arbitrary location that could be saved within a config file of some sorts.

Thanks in advance!

Cyrus
  • 69,405
  • 13
  • 65
  • 117
Evan Cooper
  • 147
  • 1
  • 8

1 Answers1

-2

According to the reference manual, tilde expansion doesn't work in quotes. The easiest alternative is to use $HOME instead.

Assuming that the code in the question is working apart from the tilde expansion (which is possible if the files in ~/Desktop all have very simple names), then you could amend your code to work without the tilde:

DEST="$HOME/Desktop/*"    

for file in "$DEST"; do
    echo "$file"
done

However, you will very likely run into filename expansion (globbing) problems if you do that. You would do best to drop the quotes everywhere, since they're not needed. Also, delay the filename expansion until it is actually needed:

DEST=~/Desktop

for file in $DEST/*; do
    echo $file
done
Paul Hicks
  • 11,495
  • 3
  • 41
  • 67
  • 3
    I suggest to replace `"$DEST"` by `$DEST`. – Cyrus Aug 23 '16 at 03:24
  • Me too, but I was making the fewest changes possible from the OPs code. – Paul Hicks Aug 23 '16 at 03:25
  • Yes, that seems to work. Thank you. I'll accept the above answer, even though it Cyrus' suggestion was needed in order for it to work. Quick question, when should/shouldn't the quotes be used? – Evan Cooper Aug 23 '16 at 03:26
  • Ideally, you'd keep your glob out of your variable. `"$DEST"/*`. – Charles Duffy Aug 23 '16 at 03:36
  • 1
    @PaulHicks, ...and I **strongly** disagree with the "in general, they're never needed". Directory names with spaces happen in the Real World. They shouldn't, perhaps, but we live in a world with desktop users who've been trained to name "folders" with meaningful multi-word human names. – Charles Duffy Aug 23 '16 at 03:37
  • @PaulHicks, ...similarly, `echo $file` has far more ways it can emit something that doesn't match actual variable contents than `echo "$file"` does. If your `IFS` value contains a character found somewhere in the filename? No longer printed. If your variable expands to contain a glob when word-split? Your output suddenly depends on what that glob expands to, and which shell options (`failglob`, `nullglob`, etc) are set. You want to echo a multi-line value? Suddenly it's all one line. Etc. – Charles Duffy Aug 23 '16 at 03:40
  • ...indeed, since we don't know what the contents of the directory are, and whether any of the caveats apply, I'm tempted to consider the claim that the quotes aren't needed sufficient to justify a downvote. – Charles Duffy Aug 23 '16 at 03:40
  • Try `mkdir 'My Documents'; touch 'My Documents/foo.txt' 'My Documents/*'; DEST='My Documents'`, and running the code given here; you'll see that failing to quote the `$DEST` used for the loop prevents the directory from being addressed at all, whereas failing to quote the expansion for the `echo` means that the `*` is expanded and so you get `foo.txt` printed twice. – Charles Duffy Aug 23 '16 at 03:43
  • Okay, following along the comments here... but I have another question for you guys, how could I assign `DEST` to the contents of a .txt file? Any combination of quotes or no quotes or having the glob on the outside of the variable don't seem to fly. Again, thanks for all the help guys – Evan Cooper Aug 23 '16 at 03:44
  • Won't that write to the file? Sorry I meant to be more clear. I'd like to have the .txt describe the destination that meant to be looped over, not list the contents that are found – Evan Cooper Aug 23 '16 at 03:49
  • Downvote: This is broken because you put the wildcard in a variable. In the general case, you cannot get this to work reliably. `for file in ~/"Evan's Desktop"/*; done` is the only way to loop over files where the path contains a space, for example. You can't do that with a variable (well, you can put `$HOME/"Evan's Desktop"` in a variable and then do `for file in "$variable"/* `, but you cannot combine proper quoting and wildcards in the same variable). – tripleee Aug 23 '16 at 03:54
  • The first solution was an amendment to the OPs solution. The second solution is preferred. I'll update the answer accordingly. – Paul Hicks Aug 23 '16 at 04:10
  • Done. Integrated other good ideas from this comment thread too. Feel free to update the answer too. It's totally open source :) – Paul Hicks Aug 23 '16 at 04:16