Reputation: 31
I am trying to read a directory with "ls" and do operations on it
directory example:
$ ls -1
x x
y y
z z
script file: myScript.sh
#!/bin/bash
files=(`ls -1`);
for ((i=0; i<"${#files[@]}"; i+=1 )); do
echo "${files[$i]}"
done
however, the output is
$ myScript.sh
x
x
y
y
z
z
yet if I define "files" in the following way
$ files=("x x" "y y" "z z")
$ for ((i=0; i<"${#files[@]}"; i+=1 )); do echo "${files[$i]}"; done
x x
y y
z z
How can I preserve the spaces in "files=(`ls -1`)"?
Upvotes: 0
Views: 2388
Reputation: 31
It seems the main conclusion is not to use ls
. Back in Pleistocene age of Unix programming, they used ls
; however, these days, ls
is best-restricted to producing human-readable displays only. A robust script for anything that can be thrown at your script (end lines, white spaces, Chinese characters mixed with Hebrew and French, or whatever), is best achieved by some form of globbing (as recommended by others here BashPitfalls).
#!/bin/bash
for file in ./*; do
[ -e "${file}" ] || continue
# do some task, for example, test if it is a directory.
if [ -d "${file}" ]; then
echo "${file}"
fi
done
The ./
is maybe not absolutely necessary, but it may help if the file begins with a "-", clarifying which file has the return line (or lines), and likely some other nasty buggers. This is also a useful template for specific files (.e.g, ./*.pdf
). For example, suppose somehow the following files are in your directory: "-t" and "<CR>
t". Then (revealing other issues with ls
when using nonstandard characters)
$ ls
-t ?t
$ for file in *; do ls "${file}"; done
-t ?t
?t
whereas:
$ for file in ./*; do ls "${file}"; done
./-t
./?t
also
$ for file in ./*; do echo "${file}"; done
./-t
./
t
A workaround with POSIX commands can be achieved by --
$ for file in *; do ls -- "${file}"; done # work around
-t
?t
Upvotes: 1
Reputation: 295291
See:
That is to say:
files=( * )
That is to say, either:
printf '%s\0' *
or
find . -mindepth 1 -maxdepth 1 -print0
will emit a NUL-delimited string, which you can load into a shell array safely using (in modern bash 4.x):
readarray -d '' array < <(find . -mindepth 1 -maxdepth 1 -print0)
...or, to support bash 3.x:
array=( )
while IFS= read -r -d '' name; do
array+=( "$name" )
done < <(find . -mindepth 1 -maxdepth 1 -print0)
In either of the above, that find
command potentially being on the other side of a FIFO, network stream, or other remoting layer (assuming that there's some complexity of that sort stopping you from using a native shell glob).
Upvotes: 5
Reputation: 3492
Try this:
eval files=($(ls -Q))
Option -Q
enables quoting of filenames.
Option -1
is implied (not needed), if the output is not a tty.
Upvotes: -4