4

Ubuntu 14.04.1.

I have a bash script, called by cron every 10 minutes, which basically looks for files in a subdir, then loops through and process each file. But how do I check if there are no files found? If there are no files found I don't want to process them, and I don't want to get an email via cron that says "no files found", because cron runs every 10 minutes. That's 144 emails per day saying "no files found" that I don't want to get.

  • The input/ dir is owned by me and has full rwx permissions.
  • I've already guaranteed the files in input/ do not contain spaces, thanks to another answer on Ask Ubuntu.

Here's my basic script.

#!/bin/bash
# Cron requires a full path in $myfullpath
myfullpath=/home/comp/progdir
files=`ls $myfullpath/input/fedex*.xlsx`
# How do I check for no files found here and exit without generating a cron email?
for fullfile in $files
do

done

Thanks! I didn't even know what to google for this one.

EDIT: My script is now this:

#!/bin/bash
# Script: gocronloop, Feb 5, 2015
# Cron requires a full path to the file.
mydir=/home/comp/perl/gilson/jimv/fedex
cd $mydir/input
# First remove spaces from filesnames in input/
find -name "* *" -type f | rename 's/ /-/g'
cd $mydir
shopt -s nullglob
if [ $? -ne 0 ]; then
    echo "ERROR in shopt"
    exit 1
fi
for fullfile in "$mydir"/input/fedex*.xlsx
    do
    # First remove file extension to get full path and base filename.
    myfile=`echo "$fullfile"|cut -d'.' -f1`
    echo -e "\nDoing $myfile..."
    # Convert file from xlsx to xls.
    ssconvert $myfile.xlsx $myfile.xls
    # Now check status in $?
    if [ $? -ne 0 ]; then
        echo "ERROR in ssconvert"
        exit 1
    fi
    perl $1 $mydir/fedex.pl -input:$mydir/$myfile.xls -progdir:$mydir 
    done
3

7 Answers 7

9

First things first: Don't parse ls.

Now that we have got that out of the way, use globbing, alongwith nullglob:

shopt -s nullglob
for fullfile in "$myfullpath"/input/fedex*.xlsx
do
#.......
done

Usually with globbing, if * doesn't match anything it's left as is. With nullglob, it is replaced with nothing, so a false match isn't triggered.

For example:

$ bash -c 'a=(foo/*); echo ${a[@]}'
foo/*
$ bash -c 'shopt -s nullglob; a=(foo/*); echo ${a[@]}'

$
10
  • Yep, better. ;-)
    – Rmano
    Feb 5, 2015 at 13:56
  • I'm getting an error in the cron email which says "shopt: not found" but when I run shopt at the command line I get output. When I do which shopt I get nothing, and there is no man page for shopt. Weird.
    – Bulrush
    Feb 5, 2015 at 14:43
  • @Bulrush shopt is a shell builtin of bash. I was under the impression your script is using bash. What does the cron entry look like, i.e., how are you calling this script?
    – muru
    Feb 5, 2015 at 14:45
  • 1
    @Bulrush, no, !/bin/bash is not something that bash -n would find, since it's not a syntax error -- it's a completely valid command (running /bin/bash, waiting for it to exit, and negating its exit status). Similarly, #/bin/bash is just a comment, and comments are valid syntax too. #!/bin/bash is read by the operating itself when deciding which interpreter to use, so as far as bash is concerned, it's just a comment. Feb 5, 2015 at 20:58
  • 1
    @Bulrush You can try checkbashisms. It throws this warning: script test.sh does not appear to have a #! interpreter line; you may get strange results.
    – muru
    Feb 6, 2015 at 13:52
2

If you're dead-set on using ls anyway, despite it's unsuitability for your original code, or if you:

just want to find out if ls didn't find any files

you could check it's exit code. A "No such file..." will fail (exit code 2). While even an empty directory's ls will succeed (exit code 0):

$ ls *.xls
ls: cannot access *.xls: No such file or directory
$ echo $?
2

$ ls
$ echo $?
0
4
  • No I don't have to use ls, as it appears ls generates an error msg if there are no files to process in input/, which generates a cron email, and I don't want cron emails filling my mail box every 10 minutes.
    – Bulrush
    Feb 5, 2015 at 14:22
  • True. But this answers your question "How to see if no output from ls *.xls command? / how do I check if there are no files found?" Don't use ls as your original code does, but in case you want to first check if there are any files (for some other reason, you want a "yes/no files" answer, etc)
    – Xen2050
    Feb 5, 2015 at 14:26
  • I appreciate your input, but I did learn that parsing ls is actually a serious problem. So in this case, I'd like to learn how to do it better, faster, stronger. :)
    – Bulrush
    Feb 5, 2015 at 15:05
  • 1
    @Xen2050 It was indeed the one answer to the actual question I had, so thanks!
    – Dmitri
    Feb 6, 2015 at 8:00
2

Let find do the hard work for you. Write a script that processes a file passed as the first parameter, then do this in your crontab:

find /wherever  -iname 'fedex*.xls' -exec your-script "{}" \;

find will not generate any output if it doesn't find files matching the expression.

2

Python seems a comfortable option as well if I am not missing the point:

#!/usr/bin/env python3
import subprocess
import os

myfullpath = "/home/jacob/Bureaublad" 
files = [f for f in os.listdir(myfullpath) if f.endswith(".xlsx")]

for f in files:
    cmd = "gedit"
    subprocess.check_call(["/bin/bash", "-c", cmd])
2
  • 1
    If you're not doing anything if len(files) is zero, you may omit that line/condition; for f in files will do nothing if files is empty; and files is guaranteed to be iterable due to it being defined as a list comprehension. One less line! \o/ Also, instead of subprocess.popen, I suggest using subprocess.check_call or subprocess.check_output; Popen semantics are more complicated than that, and you're bound to leak file descriptors and/or leave zombie processes around.
    – roadmr
    Feb 5, 2015 at 22:31
  • @roadmr completely right of course. It was a quick one to indicate python might have advantages in a case like this. Thanks for mentioning. Feb 6, 2015 at 5:59
1

If you look at https://stackoverflow.com/questions/2937407/test-whether-a-glob-has-any-matches-in-bash, something like that should work:

cd "$myfullpath/input/"

if test -n "$(shopt -s nullglob; echo fedex*.xlsl)"
then
    for file in fedex*.xlsl
    do 
          fullfile="$myfullpath/input/$file"
          # things
    done 
fi 

... Look also at http://mywiki.wooledge.org/ParsingLs

5
  • 2
    Somebody should add ls * to a blacklist for questions and automatically redirect to that wiki page.
    – muru
    Feb 5, 2015 at 13:58
  • That doesn't look like bash syntax.
    – Bulrush
    Feb 5, 2015 at 15:23
  • 1
    Rmano's snippet looks plenty like bash shellscript to me.
    – user
    Feb 5, 2015 at 16:02
  • 1
    ...it's silly and inefficient (due to the subshell), and doesn't work in all corner cases (if you had filenames that contained only whitespace, this would deny that they existed), but it's certainly valid syntax. (If you wanted to efficiently check on whether any files exist: files=( fedex*.xls ); if (( ${#files[@]} )) && [[ -e $files ]] ; then echo "Files exist"; fi -- no expensive subshell needed, and that's going to extra effort to work with nullglob either on or off). Feb 5, 2015 at 21:02
  • Yep, @CharlesDuffy is correct, and muru answer is much better. I leave the answer just for the link to Greg's wiki ;-).
    – Rmano
    Feb 6, 2015 at 8:45
1
$ if ! ls /tmp/*.bla >/dev/null 2>&1 ; then echo "no meat" ; else echo "have a steak" ; fi
no meat
$ touch /tmp/a.bla
$ if ! ls /tmp/*.bla >/dev/null 2>&1 ; then echo "no meat" ; else echo "have a steak" ; fi
have a steak
3
  • 1
    That's just a repeat of a previous answer without code formatting Feb 5, 2015 at 19:35
  • @DavidFoerster I fail to see how this is a copy of any existing answers.
    – Seth
    Feb 5, 2015 at 22:12
  • It checks the exit status of ls too, like my answer, only using an if statement (and somewhat unclear meat analogy)
    – Xen2050
    Feb 6, 2015 at 9:24
0

Why not use the find command, putting a single filename on each line:

for fullfile in $(find <dir> -name '*.xslx'); do  
# fullfile now contains the full filename, including any spaces  
# process to your heart's content, using double quotes (") around $fullfile to  
#  make sure the spaces are kept intact  
    cp "$fullfile" /new/directory/  
end
2
  • For the same reason I say "Don't parse ls."
    – muru
    Feb 6, 2015 at 2:23
  • find goes together well with xargs, especially the null-terminated options, see man find or man xargs
    – Xen2050
    Feb 6, 2015 at 9:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .