How to get a list of directories in bash and then expand them as command line options?

I am writing a bash script, which should one step get a list of directories (variables) in the target directory (which can also contain files) and then expand them as parameters for a python script.

Example:

/stuff/a dir/ /stuff/b other/ /stuff/c/ 

And I need to call in a bash script:

 script.py "a dir/" "b other/" "c/" 

or alternately, shielded spaces:

 script.py a\ dir/ b\ other/ c/ 

I need the script to be called exactly once for the "stuff" for the directory.

Is there an easy way to do this? I work in search engines, and the best that I managed to find out requires that I know how many directories there are.

+7
source share
4 answers

This is a job to search.

 find /stuff -type d -exec script.py {} + 

When you use -exec , the curly braces {} are replaced with the names of the matching files, and + indicates the end of the command (in case you want to tell find to do additional things). This is an ideal way to execute a command with find, as it will handle file names with unusual characters (e.g. spaces) correctly.

find is quite flexible, especially if you have a version of GNU that usually ships with Linux distributions.

 # Don't recurse into subdirectories. find /stuff -maxdepth 1 -type d -exec script.py {} + # Pass in a/, b/, c/ instead of /stuff/a/, /stuff/b/, /stuff/c/. find /stuff -type d -printf '%P\0' | xargs -0 script.py 

In the second example, note the careful use of \0 and xargs -0 to use the NUL character to delimit file names. It may seem strange, but it allows the team to work, even if you are doing something really strange, like using newlines \n in the names of your directories.


Alternatively, you can do this using only the built-in shells. I do not recommend this, but for educational value, here's how:

 # Start with an empty array. DIRS=() # For each file in /stuff/... for FILE in /stuff/*; do # If the file is a directory add it to the array. ("&&" is shorthand for # if/then.) [[ -d $FILE ]] && DIRS+=("$FILE") # (Normally variable expansions should have double quotes to preserve # whitespace; thanks to bash magic we don't them inside double brackets. # [[ ]] has special parsing rules.) done # Pass directories to script. The `"${array[@]}"` syntax is an unfortunately # verbose way of expanding an array into separate strings. The double quotes # and the `[@]` ensure that whitespace is preserved correctly. script.py "${DIRS[@]}" 
+13
source

A simpler solution that does not create a new process (as it finds it) is:

 for f in stuff/*; do if [ -d "$f" ]; then ./script.py "$f" fi done 
+3
source

You can use the find command and tell it to only print directories with the -type d . Your command will look like this:

 script.py $(find /stuff/* -type d) 

If you are worried about spaces and other special characters, you can do this:

 script.py $(find /stuff/* -type d | while read line; do echo "\"$line"\"; done) 
+2
source
 find /stuff/* -type d -maxdepth 1 -print0 | xargs -0 script.py 

This will find all directories under / stuff, but not recursively and pass them to script.py and make sure they are passed correctly even if there are spaces in the directory names.

+1
source

All Articles