13

How do I put the result of find $1 into an array?

In for loop:

for /f "delims=/" %%G in ('find $1') do %%G | cut -d\/ -f6-
2
  • Could you be more clear? Not sure what you ment by do %%G and for /f "delims=/" %%G Commented Nov 21, 2011 at 14:33
  • 5
    It looks like you're mixing Bash/*NIX commands & syntax with Microsoft shell commands(for /f, %%G) & syntax. Correct your example to clarify the question. Commented Nov 21, 2011 at 14:35

5 Answers 5

61

I want to cry.

In bash:

 file_list=()
 while IFS= read -d $'\0' -r file ; do
     file_list=("${file_list[@]}" "$file")
 done < <(find "$1" -print0)

 echo "${file_list[@]}"

file_list is now an array containing the results of find "$1

What's special about "field 6"? It's not clear what you were attempting to do with your cut command.

Do you want to cut each file after the 6th directory?

for file in "${file_list[@]}" ; do
    echo "$file" | cut -d/ -f6-
done

But why "field 6"? Can I presume that you actually want to return just the last element of the path?

for file in "${file_list[@]}" ; do
    echo "${file##*/}"
done

Or even

echo "${file_list[@]##*/}"

Which will give you the last path element for each path in the array. You could even do something with the result

for file in "${file_list[@]##*/}" ; do
    echo "$file"
done

Explanation of the bash program elements:

(One should probably use the builtin readarray instead)

find "$1" -print0

Find stuff and 'print the full file name on the standard output, followed by a null character'. This is important as we will split that output by the null character later.

<(find "$1" -print0)

"Process Substitution" : The output of the find subprocess is read in via a FIFO (i.e. the output of the find subprocess behaves like a file here)

while ... 
done < <(find "$1" -print0)

The output of the find subprocess is read by the while command via <

IFS= read -d $'\0' -r file

This is the while condition:

read

Read one line of input (from the find command). Returnvalue of read is 0 unless EOF is encountered, at which point while exits.

-d $'\0'

...taking as delimiter the null character (see QUOTING in bash manpage). Which is done because we used the null character using -print0 earlier.

-r

backslash is not considered an escape character as it may be part of the filename

file

Result (first word actually, which is unique here) is put into variable file

IFS= 

The command is run with IFS, the special variable which contains the characters on which read splits input into words unset. Because we don't want to split.

And inside the loop:

file_list=("${file_list[@]}" "$file")

Inside the loop, the file_list array is just grown by $file, suitably quoted.

Sign up to request clarification or add additional context in comments.

4 Comments

+1 This is the right way to do it; it handles spaces and other funny characters in filenames, which all the other posted answers fail on.
This certainly worked for me as a 'black box', but I was wondering if perhaps you (or anyone reading this) could point me in the direction of some tutorial that covers how it works (the combination of all the different parts, that is).
JonathanY.: You will want to read Parameter Substitution. and become familiar with BashFAQ.
Added explanation of program elements to @Sorpigal's answer.
13
arrayname=( $(find $1) )

I don't understand your loop question? If you look how to work with that array then in bash you can loop through all array elements like this:

for element in $(seq 0 $((${#arrayname[@]} - 1)))
do
        echo "${arrayname[$element]}"
done

4 Comments

Error: "1.sh: 3: Syntax error: "(" unexpected" this line: arrayname=( $(find /usr/dir1/dir2/dir3) )
@Gábor Varga: that error means you're using a basic shell that doesn't support arrays. Use bash instead. If this is a shell script, that means starting it with #!/bin/bash instead of #!/bin/sh.
@GáborVarga: This question is tagged bash so I presume you are using bash. If you are actually using POSIX sh or some other shell, please let us know.
Broken: fails with spaces and glob characters in file names.
0

This is probably not 100% foolproof, but it will probably work 99% of the time (I used the GNU utilities; the BSD utilities won't work without modifications; also, this was done using an ext4 filesystem):

declare -a BASH_ARRAY_VARIABLE=$(find <path> <other options> -print0 | sed -e 's/\x0$//' | awk -F'\0' 'BEGIN { printf "("; } { for (i = 1; i <= NF; i++) { printf "%c"gensub(/"/, "\\\\\"", "g", $i)"%c ", 34, 34; } } END { printf ")"; }')

Then you would iterate over it like so:

for FIND_PATH in "${BASH_ARRAY_VARIABLE[@]}"; do echo "$FIND_PATH"; done

Make sure to enclose $FIND_PATH inside double-quotes when working with the path.

1 Comment

Broken: will not handle quotes, and is subject to code injection.
0

Here's a simpler pipeless version, based on the version of user2618594

declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")")
for nm in "${names[@]}"
do
    echo "$nm"
done

2 Comments

Broken: will not handle quotes, and is subject to code injection.
somebody might use this for non internet related stuff, so code injection isn't a problem
-5

To loop through a find, you can simply use find:

for file in "`find "$1"`"; do
    echo "$file" | cut -d/ -f6-
done

It was what I got from your question.

4 Comments

This doesn't work if filenames contain spaces!
To work with filename containing spaces, simply quote the command like this: for file in "`find "$1"`"; do
I just updated the code to fix this issue ;)
Still doesn't work that way. You just go through the loop once where "file" is assigned the string with all the files that find found. Plus the use of double quotes inside double quotes is fubar.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.