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-
codeforester
  • 39,467
  • 16
  • 112
  • 140
Gábor Varga
  • 840
  • 5
  • 15
  • 25
  • Could you be more clear? Not sure what you ment by `do %%G` and `for /f "delims=/" %%G` – gustavotkg Nov 21 '11 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. – gamen Nov 21 '11 at 14:35

5 Answers5

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.

David Tonhofer
  • 14,559
  • 5
  • 55
  • 51
sorpigal
  • 25,504
  • 8
  • 57
  • 75
  • 4
    +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. – Gordon Davisson Nov 21 '11 at 16:19
  • 1
    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). – Jonathan Y. Feb 07 '14 at 20:41
  • 2
    JonathanY.: You will want to read [Parameter Substitution](http://tldp.org/LDP/abs/html/parameter-substitution.html). and become familiar with [BashFAQ](http://mywiki.wooledge.org/BashFAQ). – sorpigal Feb 17 '14 at 13:00
  • Added explanation of program elements to @Sorpigal's answer. – David Tonhofer Dec 25 '15 at 16:00
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
Cougar
  • 651
  • 4
  • 11
  • Error: "1.sh: 3: Syntax error: "(" unexpected" this line: arrayname=( $(find /usr/dir1/dir2/dir3) ) – Gábor Varga Nov 21 '11 at 14:46
  • @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`. – Gordon Davisson Nov 21 '11 at 16:18
  • 1
    @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. – sorpigal Nov 21 '11 at 17:15
  • 1
    Broken: fails with spaces and glob characters in file names. – gniourf_gniourf Dec 25 '17 at 09:10
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.

user2618594
  • 355
  • 4
  • 3
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
R Risack
  • 69
  • 8
-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.

William Luc Ritchie
  • 1,666
  • 1
  • 11
  • 15
gustavotkg
  • 4,099
  • 1
  • 19
  • 29