One thing I will say is:
Using the output from
isn't a very good way of getting a list of files to use in a script, or to pass to other commands, because the output from the
command isn't predictable, or consistent and can cause problems if file-names have spaces, or other special characters in them.
If you're using the shell in interactive mode, poking around looking for files, then by all means - use
and filter the results with
, to find what you're looking for. But you shouldn't attempt to parse the output of
in scripts in any way, shape or form.
For more information, see this link:
For getting a list of files, it's better to create/populate an array using either
with data redirected from the
command, or by using a
loop with globbing.
readarray -d '' -t fileList < <(find ./ -maxdepth 1 -type f -iname "*.txt" -print0)
In the above, the
command populates an array called
with results that are redirected from a
In the above, there are actually two redirections going on.
The left-most redirection
is redirecting a stream of data to the
command. The right-most redirection
<(find ......blah blah blah)
is a process redirection, which redirects the output from
, into the left-most redirection
. Which effectively feeds
the output from
To explain the options used with
option specifies what delimiter is to be used, so it knows where each array element starts/ends. In this case, we've used two single quotes
, which specifies nothing as a delimiter. An empty delimiter. This causes
to use any
) in the received stream of data as delimiters.
parameter, specifies that the array to create/populate is called
NOT to recurse into any sub-directories. So we'll only get results for files in the current directory.
specifies that we're only looking for files.
specifies that we're looking for any files that end with
- you should substitute
with whatever pattern you're looking for).
to append a
) to the end of each array item, so each file-name will end with a null character.
is set up to use
characters as separators, we can guarantee that each array element will contain the full filename of each file.
That way, any spaces, or special characters present in filenames will be safely preserved in the filenames in the array,with no fear of problems with shell expansions, globbing, or word-splitting.
So by using
to get the filenames and using
to populate the array, we don't risk any of the problems that could be caused by parsing and using the output from
, which can cause intermittent errors/side-effects in scripts.
So that is why the combination of
is better and safer than using the output from
The other option is to use a
loop with shell globbing:
for file in ./*.txt; do
# Ensure that we're definitely dealing with a text file
# and not a directory or some other file-system object using .txt in the name.
if [[ -f "$file" ]]; then
fileList+=("$file") # it's a file, so append it to the array
Not really a lot to say about this. This is a slightly more old-school approach.
We declare an array variable called
, we use a
loop, to loop through each file-system object in the current directory that contains
at the end of it's name.
We then check each object and ensure it is definitely
a file that we're dealing with, before storing the filename in the array.
The reason we ensure it's a file is:
Because you may have a directory that has .txt in the name, or a symbolic link, or a named pipe etc. Some other file-system object that isn't actually a file and that you might not actually want to be copying later in your script! So, in this particular situation, it's a good idea to ensure the obects we're dealing with are actually files, before storing their paths/names in the array.
Personally, I tend to use
with process redirection, because there are less lines of code required. Also
is an absolute beast
when it comes to finding files. You can set up some insanely complicated and powerful searches and define multiple actions for different types of files.
Anyway - regardless of which method you choose to use - both methods I've demonstrated will populate an array called
with the filenames of any text files in the current directory. And will do so in a better, safer, more reliable and predictable way than if you tried to parse/process the output from
Once you've got your array populated with a bunch of filenames, you can use a for loop to output the indices and filenames, before prompting the user to make a selection and perform the rest of the actions in your script.
I hope this helps!
An additional tip for preventing bugs in your scripts.... Always ensure you parse, validate and range-check ALL user input. Even if you're going to be the only user of the script!
All user input should be treated with absolute suspicion. The rule of thumb is "All user input is guilty, until proven innocent!". So when reading numeric input from a user - ensure that it is actually all numeric and that the values entered are within the expected ranges. If the user enters something out of range, or unexpected - your script needs to be able to handle it gracefully.
In my experience, users are utter bum-holes!