How to perform a for-each loop over all the files under a specified path?

LinuxBashShell

Linux Problem Overview


The following command attempts to enumerate all *.txt files in the current directory and process them one by one:

for line in "find . -iname '*.txt'"; do 
     echo $line
     ls -l $line; 
done

Why do I get the following error?:

ls: invalid option -- 'e'
Try `ls --help' for more information.

Linux Solutions


Solution 1 - Linux

Here is a better way to loop over files as it handles spaces and newlines in file names:

#!/bin/bash

find . -type f -iname "*.txt" -print0 | while IFS= read -r -d $'\0' line; do
    echo "$line"
    ls -l "$line"    
done

Solution 2 - Linux

The for-loop will iterate over each (space separated) entry on the provided string.

You do not actually execute the find command, but provide it is as string (which gets iterated by the for-loop). Instead of the double quotes use either backticks or $():

for line in $(find . -iname '*.txt'); do 
     echo "$line"
     ls -l "$line"
done

Furthermore, if your file paths/names contains spaces this method fails (since the for-loop iterates over space separated entries). Instead it is better to use the method described in dogbanes answer.


To clarify your error:

As said, for line in "find . -iname '*.txt'"; iterates over all space separated entries, which are:

  • find
  • .
  • -iname
  • '*.txt' (I think...)

The first two do not result in an error (besides the undesired behavior), but the third is problematic as it executes:

ls -l -iname

A lot of (bash) commands can combine single character options, so -iname is the same as -i -n -a -m -e. And voila: your invalid option -- 'e' error!

Solution 3 - Linux

More compact version working with spaces and newlines in the file name:

find . -iname '*.txt' -exec sh -c 'echo "{}" ; ls -l "{}"' \;

Solution 4 - Linux

Use command substitution instead of quotes to execute find instead of passing the command as a string:

for line in $(find . -iname '*.txt'); do 
     echo $line
     ls -l $line; 
done

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Question0x90View Question on Stackoverflow
Solution 1 - LinuxdogbaneView Answer on Stackoverflow
Solution 2 - LinuxVegerView Answer on Stackoverflow
Solution 3 - LinuxjserrasView Answer on Stackoverflow
Solution 4 - LinuxJens EratView Answer on Stackoverflow