Reputation: 343
let me start off with what I need; the program is given a directory, it will then examine all the files in the directory (works) and do stuff to the files (waiting till it can find all the files for this part). then it will look for subdirectories and re-run its self for each subdirectory.
the directory I'm testing with looks like this:
desktop/test_files/ (starting directory)
desktop/test_files/folder 1/
desktop/test_files/folder 1>folder 2/
desktop/test_files/folder 1>folder 2/<files, 20 or so>
desktop/test_files/folder 3/
desktop/test_files/folder 3/<more files, 20 or so>
folders and files do contain spaces in the names
the output is:
$ ./x007_shorter.sh Desktop/test_files/
Desktop/test_files/"folder 1"/
Desktop/test_files/folder 1/"folder 2"/
ls: cannot access */: No such file or directory
Desktop/test_files/folder 1/folder 2/"folder 3"/
./x007_shorter.sh: line 4: cd: ./folder 3/: No such file or directory
ls: cannot access */: No such file or directory
here is the program:
#!/bin/bash
function findir {
newDir=$1
eval cd $newDir
ARRAY=( $(ls -d */) )
declare -a diry
count=0
a=0
while [ $a -lt ${#ARRAY[@]} ]; do
diry[$count]="${ARRAY[$a]}"
noSpace=true
while [ true ]; do
if [[ ${diry[$count]} == */* ]] ; then
if [ $noSpace = false ]; then
diry[$count]="${diry[$count]:0:((${#diry[$count]}-1))}\"/"
fi
break
noSpace=true
fi
let "a=$a+1"
if [ $noSpace = false ]; then
diry[$count]="${diry[$count]} ${ARRAY[$a]}"
else
diry[$count]="\"${diry[$count]} ${ARRAY[$a]}"
fi
noSpace=false
done
let "count=$count+1"
let "a=$a+1"
done
for a in `seq 1 ${#diry[@]}`; do
eval cd .$newDir
# list "${diry[($a-1)]}"
where=`pwd`
# eval cd $newDir
#findir "${diry[($a-1)]}"
#findir "$where${diry[($a-1)]:1}"
#Right option won, echo "${diry[($a-1)]} Vs $where/${diry[($a-1)]}"
echo "$where/${diry[($a-1)]}"
findir "./${diry[($a-1)]}"
done
}
function list {
input_file_directory=$1
eval cd $input_file_directory
ARRAY=( $(find . -maxdepth 1 -type f -print) )
declare -a files
count=0
a=0
while [ $a -lt ${#ARRAY[@]} ]; do
files[$count]="${ARRAY[$a]}"
while [ true ]; do
if [[ ${ARRAY[(($a+1))]} == ./* ]] ; then
break
fi
if [[ "${ARRAY[(($a+1))]}" == "" ]] ; then
break
fi
let "a=$a+1"
files[$count]="${files[$count]} ${ARRAY[$a]}"
done
let "count=$count+1"
let "a=$a+1"
done
where=`pwd`
for a in `seq 1 ${#files[@]}`; do
echo "$where${files[($a-1)]:1}"
#going to work on each file, just echoing file till lists all files
done
}
clear
dar=""
if [[ $1 = "" ]]; then
read -p "Please enter a directory for me to scan" newdir
dar=$newdir
list $newdir
findir $newdir
else
dar=$1
list $1
findir $1
fi
Upvotes: 0
Views: 4862
Reputation: 342313
You have the error "No such file ....
due to this
ARRAY=( $(ls -d */) )
When its expanded, directories with whitespaces will get stored in array as individual elements. eg Desktop/test_files/folder 1/folder 2/"folder 3"/
.
In the array, element 0 will be Desktop/test_files/folder
, element 1 will be 1/folder
and so on. That's why your script can't find the directory.
You can set the IFS
to $'\n' before assigning to the array
OLDIFS=$IFS
IFS=$'\n'
ARRAY=($(ls -d */))
IFS="$OLDIFS"
Upvotes: 0
Reputation: 125718
Chris J's answer is the preferred way to do things if you can put the per-file stuff in a separate command(/script). If you want everything in a single script, my favorite incantation is something like this:
while IFS="" read -r -d $'\000' file <&3; do
dostuffwith "$file"
done 3< <(find -x "$dir" -mindepth 1 -type f -print0)
See BashFAQ #20 and #89 for explanations and some other options. Note that this only works in bash (i.e. the script must start with #!/bin/bash). Also, it processes the contents of a given directory in alphabetic order, rather than files-before-subdirectories.
If you really want to step through the files "by hand" (i.e. to get more control over the traversal order), here's how I'd do it:
#!/bin/bash
process_dir() {
local -a subdirs=()
echo "Scanning directory: $1"
# Scan the directory, processing files and collecting subdirs
for file in "$1"/*; do
if [[ -f "$file" ]]; then
echo "Processing file: $file"
# actually deal with the file here...
elif [[ -d "$file" ]]; then
subdirs+=("$file")
# If you don't care about processing all files before subfolders, just do:
# process_dir "$file"
fi
done
# Now go through the subdirs
for d in "${subdirs[@]}"; do
process_dir "$d"
done
}
clear
if [[ -z "$1" ]]; then
read -p "Please enter a directory for me to scan " dir
else
dir="$1"
fi
process_dir "$dir"
Upvotes: 0
Reputation: 31296
Any reason you can't use find for this? Stick the per-file operation you want in it's own script (I've called it dostufftomyfile.sh below), then do:
find $dir -type f -print0 | xargs -0 dostufftomyfile.sh
Replacing $dir with the top level directory you'll be searching from...
Edited to add... When you write the shell script, make sure you put $@ in double-quotes... e.g., you'll want your dostufftomyfile.sh script to have this structure:
#!/bin/sh
for f in "$@"
do
echo "Processing file: $f"
# Do something to file $f
done
if you don't quote $@ then the spaces in filenames will be ignored (which I suspect you won't want) :-)
Upvotes: 5