How can I list subdirectories recursively?
Here’s a nice bash script to print a directory tree, with colors: mama.indstate.edu/users/ice/bash/btree Easy to install, no root access needed.
The real question should include a description of «work», so that we can answer why ls -dR «does not work». ls -dR actually does what the documentation says: «-d Directories are listed as plain files (not searched recursively).» ls -R on the other hand does list subdirectories recursively.
6 Answers 6
Assuming you just want the name of each directory:
+1. BTW, the ‘-print’ arg is optional — it’s default. also if a specific listing format is required it can be fed into xargs to run ls with any desired options, e.g. find /path/ -type d -print0 | xargs -0 -r ls -ld . Note the -print0 for NULL terminated output, and the matching -0 xargs arg.
And if you by chance are running this on Windows and cygwin, then Windows already has a find command, so you probably should specify the path to cygwin’s bin folder.
@johnktejik, you are supposed to replace /path/ with the actual path you want to search, not type that literally.
I was looking for the same thing in the past and found this:
#!/bin/sh ####################################################### # UNIX TREE # Version: 2.3 # File: ~/apps/tree/tree.sh # # Displays Structure of Directory Hierarchy # ------------------------------------------------- # This tiny script uses "ls", "grep", and "sed" # in a single command to show the nesting of # sub-directories. The setup command for PATH # works with the Bash shell (the Mac OS X default). # # Setup: # $ cd ~/apps/tree # $ chmod u+x tree.sh # $ ln -s ~/apps/tree/tree.sh ~/bin/tree # $ echo "PATH=~/bin:\$" >> ~/.profile # # Usage: # $ tree [directory] # # Examples: # $ tree # $ tree /etc/opt # $ tree .. # # Public Domain Software -- Free to Use as You Like # http://www.centerkey.com/tree - By Dem Pilafian ####################################################### echo if [ "$1" != "" ] #if parameter exists, use as base folder then cd "$1" fi pwd ls -R | grep ":$" | \ sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/' # 1st sed: remove colons # 2nd sed: replace higher level folder names with dashes # 3rd sed: indent graph three spaces # 4th sed: replace first dash with a vertical bar if [ `ls -F -1 | grep "/" | wc -l` = 0 ] # check if no folders then echo " -> no sub-directories" fi echo exit
I wanted one that listed files as well and I learned about sed and wrote this:
#!/bin/sh ############################################# # Script that displays a recursive formatted folder and file listing # @author Corbin # @site iamcorbin.net #Folder Seperator BREAK='-------------------------------------------------------------------------------------' #Optional: if a folder is passed as an argument, run fulltree on that folder rather than the current folder if [ "$1" != "" ] then cd "$1" fi pwd ## Recursive Directory Listing with files # 1- preserve directories from being removed in 2 & 3 # 2- strip first 4 columns # 3- strip size and date # 4- prepend ' -- ' on each line # 5- remove ' -- ' from directories # 6- remove extra lines # 7- Insert a line break after directories # 8- Put a | at the beginning of all lines # 9- Indent and process 1st level sub dirs #10- Indent and process 2nd level sub dirs ls -Rhl | sed \ -e 's/^\.\//x x x x 00:00 |-/' \ -e 's/^\([^\ ]*.\)\//' \ -e 's/.*1\:8\//' \ -e 's/^/ -- /' \ -e 's/\ \ --\ \ |-//' \ -e '/--\ $/ d' \ -e '/^[^ ]/ i\'$BREAK \ -e 's/^/| /' \ | sed -e '/[^/]*\//,/'$BREAK'/ s/^|/\t&/' -e '/^\t/,/'$BREAK'/ s/'$BREAK'/\t&/' -e 's/[^/]*\//\t\| /' \ | sed -e '/[^/]*\//,/'$BREAK'/ s/^\t|/\t&/' -e '/^\t\t/,/'$BREAK'/ s/'$BREAK'/\t&/' -e 's/[^/]*\//\t\t\| /' \ | sed -e '/[^/]*\//,/'$BREAK'/ s/^\t\t/\t&/' -e 's/[^/]*\//\t\t\t\| /' echo $BREAK
How do I search all subdirectories to find one with a certain name?
Let’s say I have a top level directory called /dir and many sub directories. How do I search the subdirectories of /dir to find the one called x/x/dir/x/x/x/target ? This question is similar to, but not exactly what I am looking for: find command for certain subdirectories. I am not looking for files, just directories with a particular name.
3 Answers 3
Try find /dir -type d -name «your_dir_name» .
Replace /dir with your directory name, and replace «your_dir_name» with the name you’re looking for.
-type d will tell find to search for directories only.
For a more general solution of finding one or more directories and searching them for something like finding old email addresses in git repositories look at the following pattern:
find . -type d -name .git -print0|\ xargs -0r -I <> find <> -type f -print0 |\ xargs -0r grep -e 'my.old@email.address'
or to get one match per line:
This works out of the box in zsh. In bash, you need to run shopt -s globstar first, and beware that this also traverses symbolic links to directories. In ksh93, you need to run set -o globstar first.
If you want to match only directories or symbolic links to directories, add a trailing / (i.e. **/target/ ). In zsh, to match only directories but not symbolic links to directories, make that **/target(/) .
In any shell, you can use the find command:
On Linux and Cygwin, the . is optional. If you want to match only directories, add -type d .
How to Search for Files Recursively into Subdirectories
I am trying to look for all XML files in a particular directory and all sub-directories (recursively) inside it. ls -R *.xml is only listing files in the current directory. I am quite sure, the sub-folders themselves have several .xml files, but none are showing up. Is this a configuration issue?
5 Answers 5
. is the current directory. If you need to search in another directory, replace . with the directory path.
Does it search for the required file recursively in the directory rooted at current directory. In my case it just checked in the current directory only, didn’t check the subdirectory.
Actually I searched for .php files in current directory. But it returned only .php files in current directory, didn’t searched recursively in sub-directories. That’s why I’m asking whether find command searches recursively or not.
@mostafiz, the find command searches recursively. If you don’t quote the parameter, I think your shell might do an expansion on the * , so it will match the files in the current directory.
sudo find . -print | grep -i '.*[.]xml'
-1 for mixing find and grep , when find can do filtering using both regexes and globs, and not using find ‘s -print0 and grep’s -z when you do need to mix.
ls doesn’t have options to filter the output. For that you would need to use pipe. This passes the output from ls to grep , which then filters them to show just the .xml files.
bash
Using globstar shell option, we can make use of recursive globbing ./**/*
bash-4.3$ shopt -s globstar bash-4.3$ for i in ./**/*.xml; do printf "%s\n" "$i" ; done ./adwaita-timed.xml ./bin/hw5/stuff/book/chapter42servletexample/build/web/META-INF/context.xml ./bin/hw5/stuff/book/chapter42servletexample/build/web/WEB-INF/beans.xml ./bin/hw5/stuff/book/chapter42servletexample/build/web/WEB-INF/web.xml
Perl
Perl has a module Find , which allows for recursive directory tree traversal. Within the special find() function, we can define a wanted subroutine and the directory that we want to traverse, in this example that’s . . The one-liner in such case would be:
bash-4.3$ perl -le 'use File::Find; find(sub,".")' ./adwaita-timed.xml ./CLEAR_DESKTOP/blahblah/hw5/stuff/book/jsf2demo/build/web/WEB-INF/beans.xml ./CLEAR_DESKTOP/blahblah/hw5/stuff/book/jsf2demo/build/web/WEB-INF/web.xml ./CLEAR_DESKTOP/blahblah/hw5/stuff/book/liangweb/build.xml
Python
While Perl has a whole module dedicated to recursive tree traversal, Python has a neat function walk() that is part of os module, and repeatedly returns tuple of topmost path, list of all subdirectories, and list of filenames. We can do the following:
bash-4.3$ python -c 'import os,sys; [ sys.stdout.write(os.path.join(r,i)+"\n") for r,s,f in os.walk(".") for i in f if i.endswith(".xml") ]' ./adwaita-timed.xml ./CLEAR_DESKTOP/blahblah/hw5/stuff/book/jsf2demo/build/web/WEB-INF/beans.xml ./CLEAR_DESKTOP/blahblah/hw5/stuff/book/jsf2demo/build/web/WEB-INF/web.xml ./CLEAR_DESKTOP/blahblah/hw5/stuff/book/liangweb/build.xml
This might be far neater as a script:
#!/usr/bin/env python import os,sys for r,s,f in os.walk("."): for i in f: if i.endswith(".xml") print(os.path.join(r,i))
find
Other answers have mentioned find for recursive traversal, and that’s the go-to tool for the job. What does need mention is the fact that find has multiple command line switches, such as -printf to print output in desired format, -type f to find only regular files, -inum to search by inode number, -mtime to search by modification date, -exec <> \; to execute a particular command to process the file with passing file as argument ( where <> is standard find placeholder for current file) , and many others so please read the manpage for find .