37

Need to process files in current directory one at a time. I am looking for a way to take the output of ls or find and store the resulting value as elements of an array. This way I can manipulate the array elements as needed.

eebbesen
  • 5,070
  • 8
  • 48
  • 70
fzkl
  • 977
  • 3
  • 11
  • 17
  • @ SiegeX How do I accept the answers? I have responded and thanked those who replied the best and upvoted them when I got privilege. What more can I do? – fzkl Jan 14 '11 at 11:01

4 Answers4

54

To answer your exact question, use the following:

arr=( $(find /path/to/toplevel/dir -type f) )

Example

$ find . -type f
./test1.txt
./test2.txt
./test3.txt
$ arr=( $(find . -type f) )
$ echo ${#arr[@]}
3
$ echo ${arr[@]}
./test1.txt ./test2.txt ./test3.txt
$ echo ${arr[0]}
./test1.txt

However, if you just want to process files one at a time, you can either use find's -exec option if the script is somewhat simple, or you can do a loop over what find returns like so:

while IFS= read -r -d $'\0' file; do
  # stuff with "$file" here
done < <(find /path/to/toplevel/dir -type f -print0)
SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • 2
    @ChandanChoudhury `read -r -d $'\0'` tells the `read` command to not treat backslash's as a special escape character and to delimit each word with the NUL character since we told `find` to use that as a delimiter with `-print0`. The `IFS=` part essentially unsets the special Internal Field Separator environment variable so it will not perform word splitting on tabs or spaces or new lines. The TLDR version is that these commands make it so that you can iterate over any filename no matther what kind of characters it contains. – SiegeX Nov 29 '14 at 22:55
  • Would it work to move the `IFS=` in front of the while : `IFS = ; while ... `? –  Apr 28 '16 at 05:52
  • Also, is there a difference between this control structure and using a pipe: `find ... | while ....` ? –  Apr 28 '16 at 05:55
10
for i in `ls`; do echo $i; done;

can't get simpler than that!

edit: hmm - as per Dennis Williamson's comment, it seems you can!

edit 2: although the OP specifically asks how to parse the output of ls, I just wanted to point out that, as the commentators below have said, the correct answer is "you don't". Use for i in * or similar instead.

simon
  • 15,344
  • 5
  • 45
  • 67
  • 3
    `for i in *` is even simpler! – Dennis Williamson Jan 13 '11 at 15:32
  • 6
    [Don't parse ls!](http://mywiki.wooledge.org/ParsingLs) `for i in *; do ...` does the same thing except that it isn't confused by funny characters in filenames, and runs faster (because it doesn't spawn another process). – Gordon Davisson Jan 13 '11 at 15:44
  • 2
    @DennisWilliamson Is there any way to use a variable (containing the path) instead of `*`: `for in in $path/*` – PHP Learner Aug 25 '15 at 06:02
  • @PHPLearner: Yes. Just as you posted, but you should quote the variable. `for f in "$path"/*` – Dennis Williamson Aug 25 '15 at 12:05
  • @GordonDavisson `for i in *; do echo "$i" done` will output `*` *in empty* directory. – Alois Mahdal Jun 15 '17 at 16:15
  • @AloisMahdal Yes; shell glob expansion leaves the glob alone if there are no matching files (with `*`, that means "if there are no files"). In many cases this leads to a "file not found" type error, which if what you want. But if it's a problem in your script, either use `shopt -s nullglob` before it (and `shopt -u nullglob` after to avoid problems later on), or in a loop you can add `if [[ ! -e "$i" ]]; then continue; fi` at the beginning of the loop, so it'll skip nonexistent files. – Gordon Davisson Jun 15 '17 at 17:47
4

You actually don't need to use ls/find for files in current directory.

Just use a for loop:

for files in *; do 
    if [ -f "$files" ]; then
        # do something
    fi
done

And if you want to process hidden files too, you can set the relative option:

shopt -s dotglob

This last command works in bash only.

marco
  • 4,455
  • 1
  • 23
  • 20
  • `%files` should be `$files`. Also, if you have Bash 4.X you can set `shopt -s globstar` and use `for files in **; do` to be recursive; fully removing the use for `find` – SiegeX Jan 13 '11 at 17:03
  • Thanks! I have corrected the error; by the way, I have Bash 4, but the OP (or other readers as well) may not have it :) – marco Jan 13 '11 at 17:29
-1

Depending on what you want to do, you could use xargs:

ls directory | xargs cp -v dir2

For example. xargs will act on each item returned.

TyrantWave
  • 4,583
  • 2
  • 22
  • 25