List only Folders not files - bash

I have the following srcipt which should show me all folders in the directory, but at the moment the script list also the files under /var/www
declare -a dirs
i=1
for d in /var/www/*
do
dirs[i++]="${d%/}"
done
for((i=1;i<=${#dirs[#]};i++))
do
echo $i "${dirs[i]}"
done
What shoult i change to list only folders in the array?

How about:
printf "%s\n" /var/www/*/

You could perform a check whether it's a directory. Say:
[ -d "$d" ] && dirs[i++]="${d%/}"
instead of saying:
dirs[i++]="${d%/}"
Quoting help test:
-d FILE True if file is a directory.

for i in *
do
[ -d ${i} ] && echo $i
done

Instead of:
for d in /var/www/*
...do it like this (notice the trailing /):
for d in /var/www/*/
It will only match directories.

Related

Loop through Filenames with spaces Bash using variables

I am using the following script to iterate over a folder structure, however folders contains space in between is skipped.
for i in $(echo $File_Directory | sed "s/\// /g")
do
if [ -d $i ]
then
echo "$i directory exists."
else
echo "Creating directory $i"
`mkdir $i`
fi
done
Appreciate help on this..
Looks to me like the whole loop ought to be mkdir -p "$File_Directory" ...
But assuming he meant to put them all in the same dir:
$: File_Directory=a/b/c/d/e/f/g
$: ( IFS=/; for d in $File_Directory; do mkdir -p "$d"; done; )
That does play a little fast and loose with the parameter parsing though.

How to add a character to folder names when using the ls command?

I would like to display a character at the beginning of all folder names when typing the ls command.
So instead of this:
ls
Folder-1 Folder-2 file.txt
It displays this:
ls
📁Folder-1 📁Folder-2 file.txt
Is there a script I can write in my .bash_profile to do this?
You can make a custom script that makes it for you:
#!/bin/bash
lsmod=($(ls)) # Convert ls output to an array
for folder in ${lsmod[#]}; do # Iterate over ls results
if [[ -d $folder ]]; then # If this is a folder then...
echo "=> $folder" # Put your char
else # If not, display normally
echo "$folder"
fi
done
Folders are pointed out with a =>. Hope this helps.
One line approach:
for i in `ls`; do echo $([[ -d "$i" ]] && echo "=> $i" || echo "$i"); done;

Rename files numerically in place, multiple folders

I am able to rename files numerically, in place, in multiple folders. However, it is not the result I am looking for. My file structure looks as follows:
Pictures-
Vacation-
img.001.jpg
img.002.jpg
img.003.jpg
Holidays-
img.004.jpg
img.005.jpg
img.006.jpg
Fun-
img.007.jpg
What I'd like to achieve is:
Pictures-
Vacation-
img.001.jpg
img.002.jpg
img.003.jpg
Holidays-
img.001.jpg
img.002.jpg
img.003.jpg
Fun-
img.001.jpg
So far I have come up with the following:
a=1
for i in $vm/Holiday/*; do
new=$(printf "%03d.jpg" ${a})
mv ${i} $vm/Holiday/${new}
let a=a+1
done
How can I achieve my desired result without having to separately run this on every single directory within my pictures folder?
Take your version and make it iterate over the folders as well.
#!/bin/bash
for dir in ~/code/stack/Pictures/*; do
[ -d "${dir}" ] || continue
i=1
for img in "${dir}"/*.jpg; do
[ -e "${img}" ] || break
new="$(printf "%03d.jpg" "${i}")"
echo mv "${img}" "$(dirname "${img}")/${new}"
((i++))
done
done
Change the location of your Pictures folder and dryrun with the echo in place first. Is that what you wanted...?
try this
ls > folders #cat top folders into folders
while read folder
do
cd $folder #go to vacation folder for example
i=0
ls *.jpg > files
while read line
do
mv $line img.$i.jpg #rename files according to your need
i=$(($i+1))
done < "files"
rm files
cd ..
done < "folders"
rm folders
#!/bin/bash
function renameImages {
local i=1;
for oldFile in * ; do
if [ -d "$oldFile" ] ; then
pushd "$oldFile" >/dev/null
renameImages
popd >/dev/null
elif [ "${oldFile##*.}" == "jpg" ] ; then
newFile=$(printf img.%03d.jpg $i)
echo "$PWD/$oldFile -> $PWD/$newFile"
i=$((i+1))
fi
done
}
renameImages
This won't actually rename any files, but instead it will print on screen how it would.
If you're happy with what it does, change
echo "$PWD/$oldFile -> $PWD/$newFile"
to
mv "$PWD/$oldFile" "$PWD/$newFile"
NOTE: The first edit of this answer would rename all files, .jpg or not. This version only renames .jpgs.
#!/bin/bash
a=1
for i in $vm/Vacation/*; do
new=$(printf "%04d.jpg" ${a})
mv ${i} $vm/Vacation/${new}
let a=a+1
done
b=1
for i in $vm/Holiday/*; do
new=$(printf "%04d.jpg" ${b})
mv ${i} $vm/Holiday/${new}
let b=b+1
done
c=1
for i in $vm/Fun/*; do
new=$(printf "%04d.jpg" ${c})
mv ${i} $vm/Fun/${new}
let c=c+1
done
This is long and tedious, not ideal, but it works. With years of folders to sort and add pictures to as I come across them, a much shorter solution would have been accepted.

for-loop for every folder in a directory, excluding some of them

Thank you very much in advance for helping!
I have this code in bash:
for d in this_folder/*
do
plugin=$(basename $d)
echo $plugin'?'
read $plugin
done
Which works like a charm. For every folders inside 'this_folder', echo it as a question and store the input into a variable with the same name.
But now I'd like to exclude some folders, so for example, it will ask for every folder in that directory, ONLY if they are NOT any of the following folders: global, plugins, and css.
Any ideas how can I achieve this?
Thanks!
UPDATE:
This is how the final code looks like:
base="coordfinder|editor_and_options|global|gyro|movecamera|orientation|sa"
> vt_conf.sh
echo "# ========== Base" >> vt_conf.sh
for d in $orig_include/#($base)
do
plugin=$(basename $d)
echo "$plugin=y" >> vt_conf.sh
done
echo '' >> vt_conf.sh
echo "# ========== Optional" >> vt_conf.sh
for d in $orig_include/!($base)
do
plugin=$(basename $d)
echo "$plugin=n" >> vt_conf.sh
done
If you have a recent version of bash, you can use extended globs (shopt -s extglob):
shopt -s extglob
for d in this_folder/!(global|plugins|css)/
do
plugin=$(basename "$d")
echo $plugin'?'
read $plugin
done
You can use continue to skip one iteration of the loop:
for d in this_folder/*
do
plugin=$(basename $d)
[[ $plugin =~ ^(global|plugins|css)$ ]] && continue
echo $plugin'?'
read $plugin
done
If you meant to exclude only the directories named global, css, plugins. This might not be an elegant solution but will do what you want.
for d in this_folder/*
do
flag=1
#scan through the path if it contains that string
for i in "/css/" "/plugins/" "/global/"
do
if [[ $( echo "$d"|grep "$i" ) && $? -eq 0 ]]
then
flag=0;break;
fi
done
#Only if the directory path does NOT contain those strings proceed
if [[ $flag -eq 0 ]]
then
plugin=$(basename $d)
echo $plugin'?'
read $plugin
fi
done
You could use find and awk to build the list of directories and then store the result in a variable. Something along the lines of this (untested):
dirs=$(find this_folder -maxdepth 1 -type d -printf "%f\n" | awk '!match($0,/^(global|plugins|css)$/)')
for d in $dirs; do
# ...
done
Update 2019-05-16:
while read -r d; do
# ...
done < <(gfind -maxdepth 1 -type d -printf "%f\n" | awk '!match($0,/^(global|plugins|css)$/)')
While How to exclude some files from the loop in shell script was marked as a dupe of this Q/A and closed, that Q specifically asked about excluding files in a BASH script, which is exactly what I needed (in a script to check the validity of link fragments (the part after #) in local URLs. Here is my solution.
for FILE in *
do
## https://linuxize.com/post/how-to-check-if-string-contains-substring-in-bash/
if [[ "$FILE" == *"cnp_"* ]]
then
echo 'cnp_* file found; skipping'
continue
fi
## rest of script
done
Output:
cnp_* file found; skipping
----------------------------------------
FILE: 1 | NAME: linkchecker-test_file1.html
PATH: /mnt/Vancouver/domains/buriedtruth.com/linkchecker-tests/linkchecker-test_file1.html
RAW LINE: #bookmark1
FULL PATH: /mnt/Vancouver/domains/buriedtruth.com/linkchecker-tests/linkchecker-test_file1.html#bookmark1
LINK: /mnt/Vancouver/domains/buriedtruth.com/linkchecker-tests/linkchecker-test_file1.html
FRAGMENT: bookmark1
STATUS: OK
...
My test directory contained 3 files, with one that I wanted to exclude (web scrape of an old website: an index with with tons of deprecated link fragments).
[victoria#victoria link_fragment_tester]$ tree
.
├── cnp_members-index.html
├── linkchecker-test_file1.html -> /mnt/Vancouver/domains/buriedtruth.com/linkchecker-tests/linkchecker-test_file1.html
└── linkchecker-test_file2.html -> /mnt/Vancouver/domains/buriedtruth.com/linkchecker-tests/linkchecker-test_file2.html
0 directories, 3 files
[victoria#victoria link_fragment_tester]$
Splitting the path, checking against each foldername to be ignored.
Ignoring folder this way is convenient if you want to increase quantity of folder to be ignored afterwards. (they are here stored in ignoredfolders).
Added comments in the code.
This works fine for me in Ubuntu 20.04.2
#!/bin/bash
shopt -s globstar #necessary for search ./**/*
ignoredfolders=("folder1name" "folder2name")
for i in ./**/*
do
#pattern for splitting forward slashes into empty spaces
ARRAY=(${i//\//" "} );
for k in "${ignoredfolders[#]}"; do
#does path (splitted in ARRAY) contain a foldername ($k) to be ignored?
if [[ " ${ARRAY[#]} " =~ "$k" ]]; then
#this skips this loop and the outer loop
continue 2
fi
done
# all ignored folders are ignored, you code sits here...
#code ...
#code ...
done

Checking from shell script if a directory contains files

From a shell script, how do I check if a directory contains files?
Something similar to this
if [ -e /some/dir/* ]; then echo "huzzah"; fi;
but which works if the directory contains one or several files (the above one only works with exactly 0 or 1 files).
Three best tricks
shopt -s nullglob dotglob; f=your/dir/*; ((${#f}))
This trick is 100% bash and invokes (spawns) a sub-shell. The idea is from Bruno De Fraine and improved by teambob's comment.
files=$(shopt -s nullglob dotglob; echo your/dir/*)
if (( ${#files} ))
then
echo "contains files"
else
echo "empty (or does not exist or is a file)"
fi
Note: no difference between an empty directory and a non-existing one (and even when the provided path is a file).
There is a similar alternative and more details (and more examples) on the 'official' FAQ for #bash IRC channel:
if (shopt -s nullglob dotglob; f=(*); ((${#f[#]})))
then
echo "contains files"
else
echo "empty (or does not exist, or is a file)"
fi
[ -n "$(ls -A your/dir)" ]
This trick is inspired from nixCraft's article posted in 2007. Add 2>/dev/null to suppress the output error "No such file or directory".
See also Andrew Taylor's answer (2008) and gr8can8dian's answer (2011).
if [ -n "$(ls -A your/dir 2>/dev/null)" ]
then
echo "contains files (or is a file)"
else
echo "empty (or does not exist)"
fi
or the one-line bashism version:
[[ $(ls -A your/dir) ]] && echo "contains files" || echo "empty"
Note: ls returns $?=2 when the directory does not exist. But no difference between a file and an empty directory.
[ -n "$(find your/dir -prune -empty)" ]
This last trick is inspired from gravstar's answer where -maxdepth 0 is replaced by -prune and improved by phils's comment.
if [ -n "$(find your/dir -prune -empty 2>/dev/null)" ]
then
echo "empty (directory or file)"
else
echo "contains files (or does not exist)"
fi
a variation using -type d:
if [ -n "$(find your/dir -prune -empty -type d 2>/dev/null)" ]
then
echo "empty directory"
else
echo "contains files (or does not exist or is not a directory)"
fi
Explanation:
find -prune is similar than find -maxdepth 0 using less characters
find -empty prints the empty directories and files
find -type d prints directories only
Note: You could also replace [ -n "$(find your/dir -prune -empty)" ] by just the shorten version below:
if [ `find your/dir -prune -empty 2>/dev/null` ]
then
echo "empty (directory or file)"
else
echo "contains files (or does not exist)"
fi
This last code works most of the cases but be aware that malicious paths could express a command...
The solutions so far use ls. Here's an all bash solution:
#!/bin/bash
shopt -s nullglob dotglob # To include hidden files
files=(/some/dir/*)
if [ ${#files[#]} -gt 0 ]; then echo "huzzah"; fi
How about the following:
if find /some/dir/ -maxdepth 0 -empty | read v; then echo "Empty dir"; fi
This way there is no need for generating a complete listing of the contents of the directory. The read is both to discard the output and make the expression evaluate to true only when something is read (i.e. /some/dir/ is found empty by find).
Try:
if [ ! -z `ls /some/dir/*` ]; then echo "huzzah"; fi
Take care with directories with a lot of files! It could take a some time to evaluate the ls command.
IMO the best solution is the one that uses
find /some/dir/ -maxdepth 0 -empty
# Works on hidden files, directories and regular files
### isEmpty()
# This function takes one parameter:
# $1 is the directory to check
# Echoes "huzzah" if the directory has files
function isEmpty(){
if [ "$(ls -A $1)" ]; then
echo "huzzah"
else
echo "has no files"
fi
}
DIR="/some/dir"
if [ "$(ls -A $DIR)" ]; then
echo 'There is something alive in here'
fi
Could you compare the output of this?
ls -A /some/dir | wc -l
This may be a really late response but here is a solution that works. This line only recognizes th existance of files! It will not give you a false positive if directories exist.
if find /path/to/check/* -maxdepth 0 -type f | read
then echo "Files Exist"
fi
# Checks whether a directory contains any nonhidden files.
#
# usage: if isempty "$HOME"; then echo "Welcome home"; fi
#
isempty() {
for _ief in $1/*; do
if [ -e "$_ief" ]; then
return 1
fi
done
return 0
}
Some implementation notes:
The for loop avoids a call to an external ls process. It still reads all the directory entries once. This can only be optimized away by writing a C program that uses readdir() explicitly.
The test -e inside the loop catches the case of an empty directory, in which case the variable _ief would be assigned the value "somedir/*". Only if that file exists will the function return "nonempty"
This function will work in all POSIX implementations. But be aware that the Solaris /bin/sh doesn't fall into that category. Its test implementation doesn't support the -e flag.
This tells me if the directory is empty or if it's not, the number of files it contains.
directory="/some/dir"
number_of_files=$(ls -A $directory | wc -l)
if [ "$number_of_files" == "0" ]; then
echo "directory $directory is empty"
else
echo "directory $directory contains $number_of_files files"
fi
ZSH
I know the question was marked for bash; but, just for reference, for zsh users:
Test for non-empty directory
To check if foo is non-empty:
$ for i in foo(NF) ; do ... ; done
where, if foo is non-empty, the code in the for block will be executed.
Test for empty directory
To check if foo is empty:
$ for i in foo(N/^F) ; do ... ; done
where, if foo is empty, the code in the for block will be executed.
Notes
We did not need to quote the directory foo above, but we can do so if we need to:
$ for i in 'some directory!'(NF) ; do ... ; done
We can also test more than one object, even if it is not a directory:
$ mkdir X # empty directory
$ touch f # regular file
$ for i in X(N/^F) f(N/^F) ; do echo $i ; done # echo empty directories
X
Anything that is not a directory will just be ignored.
Extras
Since we are globbing, we can use any glob (or brace expansion):
$ mkdir X X1 X2 Y Y1 Y2 Z
$ touch Xf # create regular file
$ touch X1/f # directory X1 is not empty
$ touch Y1/.f # directory Y1 is not empty
$ ls -F # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ for i in {X,Y}*(N/^F); do printf "$i "; done; echo # print empty directories
X X2 Y Y2
We can also examine objects that are placed in an array. With the directories as above, for example:
$ ls -F # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ arr=(*) # place objects into array "arr"
$ for i in ${^arr}(N/^F); do printf "$i "; done; echo
X X2 Y Y2 Z
Thus, we can test objects that may already be set in an array parameter.
Note that the code in the for block is, obviously, executed on every directory in turn. If this is not desirable then you can simply populate an array parameter and then operate on that parameter:
$ for i in *(NF) ; do full_directories+=($i) ; done
$ do_something $full_directories
Explanation
For zsh users there is the (F) glob qualifier (see man zshexpn), which matches "full" (non-empty) directories:
$ mkdir X Y
$ touch Y/.f # Y is now not empty
$ touch f # create a regular file
$ ls -dF * # list everything in the current directory
f X/ Y/
$ ls -dF *(F) # will list only "full" directories
Y/
The qualifier (F) lists objects that match: is a directory AND is not empty. So, (^F) matches: not a directory OR is empty. Thus, (^F) alone would also list regular files, for example. Thus, as explained on the zshexp man page, we also need the (/) glob qualifier, which lists only directories:
$ mkdir X Y Z
$ touch X/f Y/.f # directories X and Y now not empty
$ for i in *(/^F) ; do echo $i ; done
Z
Thus, to check if a given directory is empty, you can therefore run:
$ mkdir X
$ for i in X(/^F) ; do echo $i ; done ; echo "finished"
X
finished
and just to be sure that a non-empty directory would not be captured:
$ mkdir Y
$ touch Y/.f
$ for i in Y(/^F) ; do echo $i ; done ; echo "finished"
zsh: no matches found: Y(/^F)
finished
Oops! Since Y is not empty, zsh finds no matches for (/^F) ("directories that are empty") and thus spits out an error message saying that no matches for the glob were found. We therefore need to suppress these possible error messages with the (N) glob qualifier:
$ mkdir Y
$ touch Y/.f
$ for i in Y(N/^F) ; do echo $i ; done ; echo "finished"
finished
Thus, for empty directories we need the qualifier (N/^F), which you can read as: "don't warn me about failures, directories that are not full".
Similarly, for non-empty directories we need the qualifier (NF), which we can likewise read as: "don't warn me about failures, full directories".
dir_is_empty() {
[ "${1##*/}" = "*" ]
}
if dir_is_empty /some/dir/* ; then
echo "huzzah"
fi
Assume you don't have a file named * into /any/dir/you/check, it should work on bash dash posh busybox sh and zsh but (for zsh) require unsetopt nomatch.
Performances should be comparable to any ls which use *(glob), I guess will be slow on directories with many nodes (my /usr/bin with 3000+ files went not that slow), will use at least memory enough to allocate all dirs/filenames (and more) as they are all passed (resolved) to the function as arguments, some shell probably have limits on number of arguments and/or length of arguments.
A portable fast O(1) zero resources way to check if a directory is empty would be nice to have.
update
The version above doesn't account for hidden files/dirs, in case some more test is required, like the is_empty from Rich’s sh (POSIX shell) tricks:
is_empty () (
cd "$1"
set -- .[!.]* ; test -f "$1" && return 1
set -- ..?* ; test -f "$1" && return 1
set -- * ; test -f "$1" && return 1
return 0 )
But, instead, I'm thinking about something like this:
dir_is_empty() {
[ "$(find "$1" -name "?*" | dd bs=$((${#1}+3)) count=1 2>/dev/null)" = "$1" ]
}
Some concern about trailing slashes differences from the argument and the find output when the dir is empty, and trailing newlines (but this should be easy to handle), sadly on my busybox sh show what is probably a bug on the find -> dd pipe with the output truncated randomically (if I used cat the output is always the same, seems to be dd with the argument count).
Taking a hint (or several) from olibre's answer, I like a Bash function:
function isEmptyDir {
[ -d $1 -a -n "$( find $1 -prune -empty 2>/dev/null )" ]
}
Because while it creates one subshell, it's as close to an O(1) solution as I can imagine and giving it a name makes it readable. I can then write
if isEmptyDir somedir
then
echo somedir is an empty directory
else
echo somedir does not exist, is not a dir, is unreadable, or is not empty
fi
As for O(1) there are outlier cases: if a large directory has had all or all but the last entry deleted, "find" may have to read the whole thing to determine whether it's empty. I believe that expected performance is O(1) but worst-case is linear in the directory size. I have not measured this.
I am surprised the wooledge guide on empty directories hasn't been mentioned. This guide, and all of wooledge really, is a must read for shell type questions.
Of note from that page:
Never try to parse ls output. Even ls -A solutions can break (e.g. on HP-UX, if you are root, ls -A does
the exact opposite of what it does if you're not root -- and no, I can't make up something that
incredibly stupid).
In fact, one may wish to avoid the direct question altogether. Usually people want to know whether a
directory is empty because they want to do something involving the files therein, etc. Look to the larger
question. For example, one of these find-based examples may be an appropriate solution:
# Bourne
find "$somedir" -type f -exec echo Found unexpected file {} \;
find "$somedir" -maxdepth 0 -empty -exec echo {} is empty. \; # GNU/BSD
find "$somedir" -type d -empty -exec cp /my/configfile {} \; # GNU/BSD
Most commonly, all that's really needed is something like this:
# Bourne
for f in ./*.mpg; do
test -f "$f" || continue
mympgviewer "$f"
done
In other words, the person asking the question may have thought an explicit empty-directory test was
needed to avoid an error message like mympgviewer: ./*.mpg: No such file or directory when in fact no
such test is required.
Small variation of Bruno's answer:
files=$(ls -1 /some/dir| wc -l)
if [ $files -gt 0 ]
then
echo "Contains files"
else
echo "Empty"
fi
It works for me
With some workaround I could find a simple way to find out whether there are files in a directory. This can extend with more with grep commands to check specifically .xml or .txt files etc. Ex : ls /some/dir | grep xml | wc -l | grep -w "0"
#!/bin/bash
if ([ $(ls /some/dir | wc -l | grep -w "0") ])
then
echo 'No files'
else
echo 'Found files'
fi
if [[ -s somedir ]]; then
echo "Files present"
fi
In my testing with bash 5.0.17, [[ -s somedir ]] will return true if somedir has any children. The same is true of [ -s somedir ]. Note that this will also return true if there are hidden files or subdirectories. It may also be filesystem-dependent.
It really feels like there should be an option to test for an empty directory.
I'll leave that editorial comment as a suggestion to the maintainers of the test command, but the counterpart exists for empty files.
In the trivial use case that brought me here, I'm not worried about looping through a huge number of files, nor am I worried about .files. I was hoping to find the aforementioned "missing" operand to test. C'est la guerre.
In the example below directory empty is empty, and full has files.
$ for f in empty/*; do test -e $f; done
$ echo $?
1
$ for f in full/*; do test -e $f; done
$ echo $?
0
Or, shorter and uglier still, but again only for relatively trivial use cases:
$ echo empty/*| grep \*
$ echo $?
1
$ echo full/* | grep \*
$ echo $?
0
So far I haven't seen an answer that uses grep which I think would give a simpler answer (with not too many weird symbols!). Here is how I would
check if any files exist in the directory using bourne shell:
this returns the number of files in a directory:
ls -l <directory> | egrep -c "^-"
you can fill in the directory path in where directory is written. The first half of the pipe ensures that the first character of output is "-" for each file. egrep then counts the number of line that start with that
symbol using regular expressions. now all you have to do is store the number you obtain and compare it using backquotes like:
#!/bin/sh
fileNum=`ls -l <directory> | egrep -c "^-"`
if [ $fileNum == x ]
then
#do what you want to do
fi
x is a variable of your choice.
Mixing prune things and last answers, I got to
find "$some_dir" -prune -empty -type d | read && echo empty || echo "not empty"
that works for paths with spaces too
Simple answer with bash:
if [[ $(ls /some/dir/) ]]; then echo "huzzah"; fi;
I would go for find:
if [ -z "$(find $dir -maxdepth 1 -type f)" ]; then
echo "$dir has NO files"
else
echo "$dir has files"
This checks the output of looking for just files in the directory, without going through the subdirectories. Then it checks the output using the -z option taken from man test:
-z STRING
the length of STRING is zero
See some outcomes:
$ mkdir aaa
$ dir="aaa"
Empty dir:
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty
Just dirs in it:
$ mkdir aaa/bbb
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty
A file in the directory:
$ touch aaa/myfile
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
$ rm aaa/myfile
A file in a subdirectory:
$ touch aaa/bbb/another_file
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty
Without calling utils like ls, find, etc.:
POSIX safe, i.e. not dependent on your Bash / xyz shell / ls / etc. version:
dir="/some/dir"
[ "$(echo $dir/*)x" != "$dir/*x" ] || [ "$(echo $dir/.[^.]*)x" != "$dir/.[^.]*x" ] || echo "empty dir"
The idea:
echo * lists non-dot files
echo .[^.]* lists dot files except of "." and ".."
if echo finds no matches, it returns the search expression, i.e. here * or .[^.]* - which both are no real strings and have to be concatenated with e.g. a letter to coerce a string
|| alternates the possibilities in a short circuit: there is at least one non-dot file or dir OR at least one dot file or dir OR the directory is empty - on execution level: "if first possibility fails, try next one, if this fails, try next one"; here technically Bash "tries to execute" echo "empty dir", put your action for empty dirs here (eg. exit).
Checked with symlinks, yet to check with more exotic possible file types.
In another thread How to test if a directory is empty with find i proposed this
[ "$(cd $dir;echo *)" = "*" ] && echo empty || echo non-empty
With the rationale that, $dir do exist because the question is "Checking from shell script if a directory contains files", and that * even on big dir is not that big, on my system /usr/bin/* is just 12Kb.
Update: Thanx #hh skladby, the fixed one.
[ "$(cd $dir;echo .* *)" = ". .. *" ] && echo empty || echo non-empty
if ls /some/dir/* >/dev/null 2>&1 ; then echo "huzzah"; fi;
to test a specific target directory
if [ -d $target_dir ]; then
ls_contents=$(ls -1 $target_dir | xargs);
if [ ! -z "$ls_contents" -a "$ls_contents" != "" ]; then
echo "is not empty";
else
echo "is empty";
fi;
else
echo "directory does not exist";
fi;
Try with command find.
Specify the directory hardcoded or as argument.
Then initiate find to search all files inside the directory.
Check if return of find is null.
Echo the data of find
#!/bin/bash
_DIR="/home/user/test/"
#_DIR=$1
_FIND=$(find $_DIR -type f )
if [ -n "$_FIND" ]
then
echo -e "$_DIR contains files or subdirs with files \n\n "
echo "$_FIND"
else
echo "empty (or does not exist)"
fi
I dislike the ls - A solutions posted. Most likely you wish to test if the directory is empty because you don't wish to delete it. The following does that. If however you just wish to log an empty file, surely deleting and recreating it is quicker then listing possibly infinite files?
This should work...
if ! rmdir ${target}
then
echo "not empty"
else
echo "empty"
mkdir ${target}
fi
Works well for me this (when dir exist):
some_dir="/some/dir with whitespace & other characters/"
if find "`echo "$some_dir"`" -maxdepth 0 -empty | read v; then echo "Empty dir"; fi
With full check:
if [ -d "$some_dir" ]; then
if find "`echo "$some_dir"`" -maxdepth 0 -empty | read v; then echo "Empty dir"; else "Dir is NOT empty" fi
fi

Resources