Bash Shell Script: Diff command conflicting line format - windows

I am using Windows OS.
My program:
#!/bin/bash
OIFS="$IFS"
IFS=$'\n'
find teste1 -type f | while read -r firstResult
do
find teste2 -type f | while read -r secondResult
do
firstName=${firstResult##*[/|\\]}
secondName=${secondResult##*[/|\\]}
if [[ "$( echo "$firstName" | tr [A-Z] [a-z])" == "$( echo "$secondName" | tr [A-Z] [a-z])" ]]; then
echo "$firstResult" "$secondResult" >> equal
else
echo "$firstResult" "$secondResult" >> notEqual
fi
if [[ $firstName == $secondName ]]; then
echo "$firstResult" "$secondResult" >> equal2
fi
done
done
diff -2 "--line-format=%L" "--unchanged-line-format=" equal equal2 > renamedFiles.lst
rm equal
rm equal2
rm notEqual
Whenever I run this program, it says "diff: conflicting line format". However, it produces the "renamedFiles.lst" and produces exactly the way I want. So, why is it giving me this answer? Can I fix it? It doesn't really affect my program but no one likes seeing warnings / errors on their programs, right? :)

I think it's because you are using both --line-format (to format all lines) and --unchanged-line-format (to format unchanged lines). I guess diff doesn't define what to do if it gets conflicting format specifiers, so it fails and tells you about it. What you could do is use for example --old-line-format=%L --new-line-format=%L --unchanged-line-format=

Related

Unix Bash Shell Scripting File Size

I am working on a bash shell script which requires me to display files in order of size in a given directory. If the size of a file is 0, I am to ask the user if they would like to delete it. So far I have this:
#!/bin/bash
FILE=$(ls -S $1)
for FIL in ${FILE}
do
echo ${FIL}
done
This displays the files in order of size, but I am unsure how to prompt the user to erase the files with a size of 0.
Thanks for your help!
find /your/path/ -size 0 -exec echo rm -i {} \; # will fail if there are spaces in any file names
better way:
find /your/path/ -size 0 -print0 | xargs -0 rm -i
Remove the echo to delete the files
Thanks #Will, #AdamKatz.
So if we want to stay as close as possible to your current approach, we could do it like this:
#!/bin/bash
FILE="$(ls -S "$1")"
for f in $FILE
do
file_size_bytes=$(du "$f" | cut -f1)
echo "$f"
if [[ "$file_size_bytes" -eq 0 ]]
then
read -r -p "Would you like to delete the zero-byte file ${f}? [Y/n]: " input
if [[ "$input" = [Yy] ]]
then
rm "$f"
fi
fi
done
Another answer used stat, but stat isn't POSIX or portable, but if you're only running under Linux, stat is a good approach.
In the above example, read -p is used to prompt the user for input, and store the result in $input. We use [[ "$input" = [Yy] ]] to see if the input is either Y or y.
The way it's currently written, you have to type y or Y and press enter to delete the file. If you want it to happen as soon as the user hits y or Y, add -n 1 to read to make it only read one character.
You also don't need to use ${var} unless you're putting it inside another string, or if you need to use some kind of parameter expansion.
As a side note, this sounds like it's some type of homework or learning experience, so, please look up every command, option, and syntax element in the above and really learn how it works.
You can make use of redirection and redirect stdin to another file descriptor while feeding the loop with process substitution to accomplish your goal. e.g.:
#!/bin/bash
[ -z "$1" ] && {
printf "error: insufficient input, usage: %s <path>\n" "${0//*\/}"
exit 0;
}
exec 3<&0 # temprorary redirection of stdin to fd 3
while read -r line; do
printf " rm '%s' ? " "$line"
read -u 3 ans # read answer for fd 3
anslower="${ans,,}"
if [ "${anslower:0:1}" = "y" ]; then
printf " %s => removed.\n" "$line"
# rm "$line"
else
printf " %s => unchanged.\n" "$line"
fi
done < <(find "$1" -type f -size 0)
exec 3<&- # close temporary redirection
note: the actual rm command is commented out to insure you don't remove wanted files by accident until your testing is complete.
Example Use/Output
$ bash findzerosz.sh ../tmp/stack/dat/tmp/tst/tdir
rm '../tmp/stack/dat/tmp/tst/tdir/file4.html' ? n
../tmp/stack/dat/tmp/tst/tdir/file4.html => unchanged.
rm '../tmp/stack/dat/tmp/tst/tdir/file1.html' ? y
../tmp/stack/dat/tmp/tst/tdir/file1.html => removed.
rm '../tmp/stack/dat/tmp/tst/tdir/file2.html' ? y
../tmp/stack/dat/tmp/tst/tdir/file2.html => removed.
rm '../tmp/stack/dat/tmp/tst/tdir/file3.html' ? Y
../tmp/stack/dat/tmp/tst/tdir/file3.html => removed.
rm '../tmp/stack/dat/tmp/tst/tdir/file5.html' ? n
../tmp/stack/dat/tmp/tst/tdir/file5.html => unchanged.
This will work, to test if one file size is 0 (you just need to include it in your loop).
myfilesize=`stat -c %s "$FIL"`
if [ $myfilesize = 0 ];then
echo "the file size is zero, do you want to delete it ?"
read -p "yes/no? " -n 1 -r
echo #Move to Next line
if [[ $REPLY =~ ^[Yy]$ ]]
then
rm "$FIL"
fi
else
echo "File size is not Zero"
fi

bash: error when counting number of files and comparing

I am new to bash scripts. I would like to compare the number of files I have in another directory using ls. And then I need to compare the number of files there with a variable I have.
if [ (cd reports_dir/ && ls) | wc -gt $MAXIMUM_REPORTS ]; then
echo hello
fi
This code gives the errors:
./monitor.sh: line 70: syntax error near unexpected token `cd'
./monitor.sh: line 70: ` if [(cd reports_dir/ && ls) | wc -gt $MAXIMUM_REPORTS]; then'
I have idea why cd is unexpected. The command (cd reports_dir/ && ls) | wc works when I run it in the terminal.
This command would be running in a while loop so it will be called repeatedly. Thus I cannot actually cd into the directory as it attempts to cd more than once, resulting in an error.
techraf's helpful answer explains the problem with the OP's approach and offers an effective solution based on ls.
Since only counting of files is required in this case, use of ls ... | wc -l should work as expected; however, it is generally preferable to work with pathname expansion (globbing) in Bash:
Globbing can safely be used to collect filenames in an array and to drive a for loop.
You can do neither robustly with $(ls ...) - see http://mywiki.wooledge.org/ParsingLs
Globbing, since it is a Bash-internal feature, is more efficient than using ls, which is an external utility.
The following solutions demonstrates the use of pathname expansion (globbing) as an alternative to ls:
Note: By default, globbing does not include hidden files; set shopt -s dotglob (temporarily) to include them.
if (( $(shopt -s nullglob; set -- reports_dir/*; echo $#) > $MAXIMUM_REPORTS )); then
echo hello
fi
shopt -s nullglob ensures that nothing is returned in case the directory is empty.
set -- reports_dir/* sets the positional parameters ($1, ...) to the result of the pathname expansion.
$# returns the count of set parameters, which in this case reflects the count of files.
The command fails because you need to use command substitution syntax, otherwise it expects a value.
As you noted, you shouldn't be changing directory with cd - you can use the directory as an argument to ls. Also wc command by default returns a count for the number of lines, words, and bytes and in your expression you need only the first one, so you should add an argument wc -l.
Other than that you need to decide if you want to include hidden files and use ls -A <dir> | wc -l. -A makes ls to print all files including hidden ones, but excluding default . and ...
Finally use the double parentheses for arithmetical comparison in the condition clause:
if (( $(ls -A reports_dir | wc -l) > $MAXIMUM_REPORTS )); then
echo hello, but read the comment below
fi
Having said that, the above recipe will work in majority of cases, however mklement0's answer contains the correct solution.
Here is another one , cd into a given directory, get count compare with $MAXIMUM_REPORTS and take it from there. passes both test cases.
[za- tools]$ MAXIMUM_REPORTS=2;
mydir=font-awesome-4.3.0/ ;
if [[ $(cd $mydir && ls -1 $# | wc -l) -gt $MAXIMUM_REPORTS ]]; then
echo "hello" ;
else echo "byeee" ; fi
Outputs:
hello
[za tools]$ MAXIMUM_REPORTS=18;
mydir=font-awesome-4.3.0/ ;
if [[ $(cd $mydir && ls -1 $# | wc -l) -gt $MAXIMUM_REPORTS ]]; then
echo "hello" ;
else echo "byeee" ;
fi
outputs:
byeee
OR:
file_count=$(ls $mydir 2>/dev/null | wc -l) ;
if [ $file_count -gt $MAXIMUM_REPORTS ]; then echo "hello" ; else echo "byee" ; fi
Simulate wc -l with sed
count=$(ls $mydir 2>/dev/null | sed -n '$=' ) ;
if [ $count -gt $MAXIMUM_REPORTS ]; then echo "$count greater then $MAXIMUM_REPORTS" ;
else echo "$count less $MAXIMUM_REPORTS" ; fi

Infinite while-loop in BASH script

I'm really struggling to see why this while-loop never ends, when the loop starts, my variable LOC is set to Testing/, which is a directory I created to test this program, it has the following layout:
I want the loop to end once all Directories have had the "count" function applied to them.
Here are the things I have tried;
I've checked my count function, and it doesn't produce an infinite loop
I've tried running through the algorithm by hand
PARSE=1
LOC=$LOC/
count
AVAILABLEDIR=$(ls $LOC -AFl | sed "1 d" | grep "/$" | awk '{ print $9 }')
while [ $PARSE = "1" ]
do
if [[ ${AVAILABLEDIR[#]} == '' ]]; then
PARSE=0
fi
DIRBASE=$LOC
for a in ${AVAILABLEDIR[#]}; do
LOC="${DIRBASE}${a}"
LOCLIST="$LOCLIST $LOC"
count
done
for a in ${LOCLIST[#]}; do
TMPAVAILABLEDIR=$(ls $a -AFl | sed "1 d" | grep "/$" | awk '{ print $9 }')
PREPEND=$a
if [[ ${TMPAVAILABLEDIR[#]} == '' ]]; then
continue
fi
for a in ${TMPAVAILABLEDIR[#]}; do
TMPAVAILABLEDIR2="$TMPAVAILABLEDIR2 ${PREPEND[#]}${a}"
done
NEWAVAILABLEDIR="$NEWAVAILABLEDIR $TMPAVAILABLEDIR2"
done
AVAILABLEDIR=$NEWAVAILABLEDIR
NEWAVAILABLEDIR=''
LOC=''
done
I am really struggling, and any input would be greatly appreciated, I've been trying to figure this out for the last couple of hours.
You should try to run the script with argument -x, or write it into the first line:
#!/bin/bash -x
Then it tells you everything it does.
In that case, you might notice two errors:
You never reset TMPAVAILABLEDIR2
You do ls on regular files as well.
If you really must avoid recursion, try this. It completely recursion-free:
#!/bin/bash
count() {
echo counting "$1"
}
todo=(Testing)
while test ${#todo[#]} != 0
do
doit=("${todo[#]}")
todo=()
for dir in "${doit[#]}"
do
for entry in "$dir"/* # If the directory is empty, this shows an entry named "*"
do
test -e "$entry" || continue # Skip the entry "*" of an empty directory
count "$entry"
test -d "$entry" || continue
todo+=("$entry")
done
done
done
You wrote you want to perform "count" on all directories.
Look at the options of find:
find $LOC -type d | while read dir; do
cd $LOC
cd ${dir}
count
done
Or shorter (when your function count accepts a directory as parameter 1):
find $LOC -type d | xargs count
I now see you do not want to use find or ls -R (recursive function). Then you should make your own recursive function, something like
function parseDir {
ls -d */ $1 | while read dir; do
count
parseDir $1/$dir
done
}
I have no idea if this will work, but it’s an interesting question I couldn't stop thinking about.
while true ; do
for word in "$(echo *)" ; do
if [[ -d "$word" ]] ; then
d[$((i++))]="$PWD"/"$word"
elif [[ -f "$word" ]] ;then
f[$((j++))]="$PWD"/"$word"
fi
done
[[ $k -gt $i ]] && cd ..
cd "$d[$((k++))]" || break
done

While loop does not execute

I currently have this code:
listing=$(find "$PWD")
fullnames=""
while read listing;
do
if [ -f "$listing" ]
then
path=`echo "$listing" | awk -F/ '{print $(NF)}'`
fullnames="$fullnames $path"
echo $fullnames
fi
done
For some reason, this script isn't working, and I think it has something to do with the way that I'm writing the while loop / declaring listing. Basically, the code is supposed to pull out the actual names of the files, i.e. blah.txt, from the find $PWD.
read listing does not read a value from the string listing; it sets the value of listing with a line read from standard input. Try this:
# Ignoring the possibility of file names that contain newlines
while read; do
[[ -f $REPLY ]] || continue
path=${REPLY##*/}
fullnames+=( $path )
echo "${fullnames[#]}"
done < <( find "$PWD" )
With bash 4 or later, you can simplify this with
shopt -s globstar
for f in **/*; do
[[ -f $f ]] || continue
path+=( "$f" )
done
fullnames=${paths[#]##*/}

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