How to get the list of files in a directory in a shell script?

783,675

Solution 1

search_dir=/the/path/to/base/dir/
for entry in "$search_dir"/*
do
  echo "$entry"
done

Solution 2

This is a way to do it where the syntax is simpler for me to understand:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./ is the current working directory but could be replaced with any path
*.txt returns anything.txt
You can check what will be listed easily by typing the ls command straight into the terminal.

Basically, you create a variable yourfilenames containing everything the list command returns as a separate element, and then you loop through it. The loop creates a temporary variable eachfile that contains a single element of the variable it's looping through, in this case a filename. This isn't necessarily better than the other answers, but I find it intuitive because I'm already familiar with the ls command and the for loop syntax.

Solution 3

The other answers on here are great and answer your question, but this is the top google result for "bash get list of files in directory", (which I was looking for to save a list of files) so I thought I would post an answer to that problem:

ls $search_path > filename.txt

If you want only a certain type (e.g. any .txt files):

ls $search_path | grep *.txt > filename.txt

Note that $search_path is optional; ls > filename.txt will do the current directory.

Solution 4

for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done

Solution 5

$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

Variations

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Notes:

  • . : current folder
  • remove -maxdepth 1 to search recursively
  • -type f : find files, not directories (d)
  • -not -path '*/\.*' : do not return .hidden_files
  • sed 's/^\.\///g' : remove the prepended ./ from the result list
Share:
783,675

Related videos on Youtube

jrharshath
Author by

jrharshath

Updated on March 05, 2022

Comments

  • jrharshath
    jrharshath over 2 years

    I'm trying to get the contents of a directory using shell script.

    My script is:

    for entry in `ls $search_dir`; do
        echo $entry
    done
    

    where $search_dir is a relative path. However, $search_dir contains many files with whitespaces in their names. In that case, this script does not run as expected.

    I know I could use for entry in *, but that would only work for my current directory.

    I know I can change to that directory, use for entry in * then change back, but my particular situation prevents me from doing that.

    I have two relative paths $search_dir and $work_dir, and I have to work on both simultaneously, reading them creating/deleting files in them etc.

    So what do I do now?

    PS: I use bash.

  • drkg4b
    drkg4b over 8 years
    I know this is pretty old but I can't seem to get the last xargs -0 -i echo "{}" command, care to explain me a bit? In particular what is the -i echo "{}" part do? Also I read from the man page that -i is deprecated now and we should use -I insted.
  • Noel Yap
    Noel Yap over 8 years
    -i substitutes {} with the arg.
  • drkg4b
    drkg4b over 8 years
    Thanks! This is useful, also for the slow minded like me I think that the {} is the string that is replaced with the matches by the find command.
  • Noel Yap
    Noel Yap over 8 years
    It's what's replaced by xargs, not find.
  • Noel Yap
    Noel Yap over 8 years
    I've updated the answer to use -I. Thanks for the heads up about the deprecation of -i.
  • gniourf_gniourf
    gniourf_gniourf over 8 years
    why do you use xargs? by default, find prints what it finds... you could delete everything from -print0.
  • mrgloom
    mrgloom over 8 years
    Can you explain why for entry in "$search_dir/*" don't work? Why we need to place /* outside of quotes?
  • Ignacio Vazquez-Abrams
    Ignacio Vazquez-Abrams over 8 years
    @mrgloom: Because you need to let the shell glob the wildcard.
  • Alexej Magura
    Alexej Magura over 7 years
    wouldn't find be faster?
  • Ignacio Vazquez-Abrams
    Ignacio Vazquez-Abrams over 7 years
    @AlexejMagura: find is a separate process. Globbing happens in the shell.
  • Victor Zamanian
    Victor Zamanian about 7 years
    No need to use grep to get only .txt files: `ls $search_path/*.txt > filename.txt'. But more importantly, one should not use the output of the ls command to parse file names.
  • ThatsRightJack
    ThatsRightJack almost 7 years
    The solution gives the full path. What if I just wanted to list what was in the current directory?
  • Ignacio Vazquez-Abrams
    Ignacio Vazquez-Abrams almost 7 years
    Just omit the path.
  • funilrys
    funilrys almost 7 years
    @mrgloom if you want to do so you can achieve that with for entry in "${search_dir}/*"
  • CallMeLoki
    CallMeLoki over 6 years
    it doesn't work if a file is space seprated(consider each as an entry)
  • Agile Bean
    Agile Bean about 6 years
    on MacOs, you don't need the parentheses - just write: for entry in ~/gcloud_installs/*
  • Soren Bjornstad
    Soren Bjornstad almost 6 years
    This works OK for a quick, informal script or one-liner, but it will break if a filename contains newlines, unlike the glob-based solutions.
  • rrr
    rrr almost 6 years
    @SorenBjornstad thanks for the advice! I didn't know newlines were permitted in filenames- what kind of files might have them? Like, is this something that occurs commonly?
  • Soren Bjornstad
    Soren Bjornstad almost 6 years
    Newlines in filenames are evil for this reason and as far as I know there's no legitimate reason to use them. I've never seen one in the wild myself. That said, it's totally possible to maliciously construct filenames with newlines in such a way as to exploit this. (For instance, imagine a directory containing files A, B, and C. You create files called B\nC and D, then choose to delete them. Software that doesn't handle this right could end up deleting preexisting files B and C instead even if you didn't have permission to do that.)
  • Noel Yap
    Noel Yap almost 6 years
    Doing that wouldn't handle file entries with spaces well.
  • samurai_jane
    samurai_jane over 5 years
    @VictorZamanian, can you elaborate why we should not use the output of ls to parse filenames? Haven't heard of this before.
  • Victor Zamanian
    Victor Zamanian over 5 years
    @samurai_jane There's a lot of links to provide regarding this topic, but here's one first search result: mywiki.wooledge.org/ParsingLs. I even saw a question here on SO claiming the reasons for not parsing the output of ls were BS and was very elaborative about it. But the replies/answers still claimed it was a bad idea. Have a look: unix.stackexchange.com/questions/128985/…
  • Dmitry Gonchar
    Dmitry Gonchar about 5 years
    This does not work recursively. For a recursive solution find is better. See this answer stackoverflow.com/a/55817578/1343917
  • Martin Jambon
    Martin Jambon almost 4 years
    This doesn't work (in bash with the default settings) if the folder is empty or if some files start with a period.
  • Georodin
    Georodin almost 4 years
    Is there a way to implement the | grep *.txt from the answer below to this snippet?
  • tripleee
    tripleee over 3 years
    @Georodin Just use "$search_dir"/*.txt if that's what you want.
  • tripleee
    tripleee over 3 years
    for [ z is a syntax error. if [ test is silly and wrong. Not quoting "$z" is a quoting error.
  • tripleee
    tripleee over 3 years
    mywiki.wooledge.org/ParsingLs explains a large number of pitfalls with this approach. You should basically never use ls in scripts. It's silly anyway; the shell has already expanded the wildcard by the time ls runs.
  • Inian
    Inian over 3 years
    @Anish B: Stop vandalizing the original post. The answer was right as it is written
  • Inian
    Inian over 3 years
    @AnishB.: No the answer as it was written should work fine for a directory name assigned to search_dir variable
  • Inian
    Inian over 3 years
    @AnishB.: If you have a problem using the attempt, then something is not right at your end. Suggest asking a new question to resolve it, instead of modifying a highly rated answer incorrectly (Also check if your * is outside of double quotes)
  • Inian
    Inian over 3 years
    There is nothing wrong with the quoting of the search_dir variable. See When to wrap quotes around a shell variable?, i.e. to protect against directory names containing spaces
  • Anish B.
    Anish B. over 3 years
    @Inian I got the issue. Yes, you are right.
  • SnoopDogg
    SnoopDogg over 3 years
    Please explain. I successfully ran the code back when I wrote the answer. If you’re going to necropost at least be useful.
  • tripleee
    tripleee over 3 years
    What's to explain? If you tested this back then, you were definitely not using a standard shell. I already pointed out the errors, and it should not be hard to look up what the correct syntax looks like, from some of the other answers here even. But check out this demo: ideone.com/ERcu2c and fork it to your heart's content.
  • tripleee
    tripleee over 3 years
    If you think this is irrelevant because it's old, you don't understand how Stack Overflow works. This is regularly - probably daily - linked as the canonical answer for new users who post duplicates of this common question, but also gets probably 100x that traffic from Google searches.
  • Admin
    Admin almost 3 years
    As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
  • GG2
    GG2 over 2 years
    In addition to the comment by @MartinJambon, the "${search_dir}/*" doesn't seem to work at all for RedHat 7.9. If search_dir is empty (for any variation) or if using the latter variation, I confusingly get the contents of the current directory (assuming search_dir is not the current dir).
  • Nick Bull
    Nick Bull over 2 years
    @GG2 You may want to initially check that the variable is set correctly before proceeding: if test -z "$search_dir"; then echo "No search directory provided"; exit 1; fi
  • GG2
    GG2 over 2 years
    @NickBull I can't remember exactly how I was trying this originally. If I revisit this now, I don't get exactly the same results. If I let an empty string past, it lists contents of root. For an empty dir or containing only files starting with ".", it just echoes the evaluated value of "${search_dir}/*", e.g. empty_dir/*. It only lists the current directory if search_dir="." We'll chalk it up to something I did wrong.
  • Jeremy Caney
    Jeremy Caney over 2 years
    There are eight existing answers to this question, including a top-voted, accepted answer with over three hundred votes. Are you certain your solution hasn't already been given? If not, why do you believe your approach improves upon the existing proposals, which have been validated by the community? Offering an explanation is always useful on Stack Overflow, but it's especially important where the question has been resolved to the satisfaction of both the OP and the community. Help readers out by explaining what your answer does different and when it might be preferred.
  • Gabriel Staples
    Gabriel Staples over 2 years
    Here are some other approaches I just added with find and ls where I capture the output into some bash arrays with mapfile and then customize and print the contents of the arrays.