I'm new to shell (bash) script. On terminal, find
behaves as:
find . -name 'Ado*'
./Adobe ReaderScreenSnapz001.jpg
./Adobe ReaderScreenSnapz002.jpg
However, my shell script seems to separate the file name by the space after "Adobe".
Code:
#!/bin/sh
i=1
for base_name in `find . -name "Ado*"`
do
echo $base_name
:
i=$((i+1))
done
It returns something like:
./Adobe
ReaderScreenSnapz001.jpg
:
What's wrong where?
The
find
result containswhite-spaces
.So the following
for loop
iterate over4
items.You can change the script to:
Most of the answers so far have used
find ... | while read ...
which works, but the while loop runs in a subshell (because it's part of a pipeline), and so your count of files ($i) is lost when the subshell exits. If you're using bash (i.e. your shebang must be#!/bin/bash
, not#!/bin/sh
), you can use its process substitution feature to avoid this problem:I've also added some other tricks here to make this more robust: I set IFS to null (just for the read command) so it won't trim leading or trailing whitespace from filenames; I use read's
-r
option so it won't do funny things with backslashes in filenames; I use find's-print0
and read's-d ''
to use null bytes as the file delimiter, so it won't even be confused by linefeeds in filenames; I use double-quotes around$base_name
inside the loop, so it won't get split on whitespace there either (not important for echo, but it'll matter if you actually try to use the filenames as filenames); finally, I pass the file list via fd #3 instead of #0 (stdin) with3<
and read's-u3
option, so in case anything inside the loop reads from stdin it won't accidentally inhale a pile of filenames.There is nothing wrong in your shell script.
When you call a program between back quotes the command is executed in a forked process, and the result is returned to the main program to be lexed and parsed. And as a string the result is separated into several token.
You should use read builtin like that
Your shell replaces
for base_name in $(find . -name "Ado*")
byfor base_name in ./Adobe ReaderScreenSnapz001.jpg ./Adobe ReaderScreenSnapz002.jpg
Therefore,
for
sees four words:-
./Adobe
-
ReaderScreenSnapz001.jpg
-
./Adobe
-
ReaderScreenSnapz002.jpg
My advice: replace
for base_name in
bywhile read base_name
read
reads line after line and stops thewhile
loop at the end of its input (find
).read
stores each line inbase_name
variable.