1

I have created the following code. At the end I want to process each file that is in the array individually but somewhere in this code I have not included the double quotes so the file names with a space in the middle it treats as 2 entries in the array:

#!/bin/bash

EXT=(sh mkv txt)

EXT_OPTS=()
# Now build the find command from the array
for i in "${EXT[@]}"; do
    EXT_OPTS+=( -o -iname "*.$i" )
done

# remove the first thing in EXT_OPTS
EXT_OPTS=( "${EXT_OPTS[@]:1}" )

# Modify to add things to ignore:
EXT_OPTS=( '(' "${EXT_OPTS[@]}" ')' ! '(' -iname "*test*" -o -iname "*sample*" ')' )

#echo "${EXT_OPTS[@]}"

searchResults=($(find . -type f "${EXT_OPTS[@]}"))

#echo "$searchResults"

for R in "${searchResults[@]}"; do
    echo "$R"
    sleep 1
done

So the results I am getting are:

./Find2.sh
./untitled
2.sh
./countFiles.sh
./unrar.sh
./untitled
3.sh
./untitled
4.sh
./clearRAM.sh
./bash_test.sh
./Test_Log.txt
./untitled.txt
./Find.txt
./findTestscript.sh
./untitled.sh
./unrarTest.sh
./Test.sh
./Find.sh
./Test_Log
copy.txt
./untitled
5.sh
./IF2.sh

As an example untitled 5.sh has been added to the array as 2 entries. where have I forgot to add the "s

cheers

EDIT with suggested edits:

#!/bin/bash

EXT=(sh mkv txt)

EXT_OPTS=()
# Now build the find command from the array
for i in "${EXT[@]}"; do
    EXT_OPTS+=( -o -iname "*.$i" )
done

# remove the first thing in EXT_OPTS
EXT_OPTS=( "${EXT_OPTS[@]:1}" )

# Modify to add things to ignore:
#EXT_OPTS=( "${EXT_OPTS[@]:-1}" )
EXT_OPTS=( '(' "${EXT_OPTS[@]}" ')' ! '(' -iname "*x0r*" -o -iname "*torrent*" ')' )

#echo "${EXT_OPTS[@]}"

#searchResults=($(find . -type f "${EXT_OPTS[@]}"))

#echo "$searchResults"

#for R in "${searchResults[@]}"; do
#   echo "$R"
#   sleep 1
#done


find . -type f "${EXT_OPTS[@]}" -exec sh -c '
    for pathname do
        printf "%s\n" "$pathname"
        sleep 1
    done' sh {} +

Now Produces:

./Find2.sh
./untitled 2.sh
./countFiles.sh
./unrar.sh
./untitled 3.sh
./Find3.sh
./untitled 4.sh
./clearRAM.sh
./bash_test.sh
./Test_Log.txt
./untitled.txt
./Find.txt
./findTestscript.sh
./untitled.sh
./unrarTest.sh
./Test.sh
./Find.sh
./Test_Log copy.txt
./untitled 5.sh
./IF2.sh
./Find4.sh
2

No, you should not parse the output of find. If you want to do something with the pathnames that find finds, you should do that from within find:

find ...stuff... -exec sh -c '
    for pathname do
        printf "%s\n" "$pathname"
        sleep 1
    done' sh {} +

Here, find will call the in-line shell script with batches of found pathnames (the script may be called more than once). find acts as a sort of pathname generator for the internal script.

Another approach is to use -print0 to output the pathnames with \0 in-between them, and then read them with a tool that knows how to read the nul-terminated pathnames. The nul-character \0 is the only character that is not allowed to be part of a Unix pathname, so that's the only safe way to pass them to another tool.

Related:

  • I see..... I have not been writing bash for very long so i'm still learning. what I was trying to do was set up the next bit to deal with the various file types. For example if it was a .sh or a .txt files to be copied to one folder and additional to that all the .tmp files go to another folder and then .log files to another folder. What function do I need to read about to do that ? I was going to use a case function but is that the best way to do it? – madmiddle Feb 8 at 22:53
  • @madmiddle To simplify it, I would make one find for each case. find ... -type f -name '*.tmp' -exec mv -t tmp-files {} + for .tmp files, for example. – Kusalananda Feb 8 at 22:58
  • @madmiddle Though you could do it in one huge find command with \( -name '*.tmp' -exec mv ... \) -o \( ... \) -o \( ... \) etc., but it wouldbe ugly and hard to maintain. – Kusalananda Feb 8 at 23:00
  • thats why I started with the array at the beginning and if i needed to add a new file type then all i needed to do was add to the array at the top and then again at a case statement at the bottom. But like i said I'm still very new to this. – madmiddle Feb 8 at 23:03
  • @madmiddle The issue is fully described and explained in the first question I linked to in my answer. The shell will split all found pathnames on spaces, tabs and newlines (by default) and your loop will loop over these split-up words. – Kusalananda Feb 8 at 23:04
1

With newer versions of bash (> 4.4 IIRC), the mapfile builtin allows you to specify a null delimter - you can then use find ... print0 as suggested in Kusalananda's answer e.g.

mapfile -t -d '' searchResults < <(find . -type f "${EXT_OPTS[@]}" -print0)

If that's not an option, then a slower uglier way would be something like

while IFS= read -r -d '' f; do 
  searchResults+=("$f")
done < <(find . -type f "${EXT_OPTS[@]}" -print0)

See the related bash: whitespace-safe procedural use of find into select

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.