0

I'm trying to set up an sh script that will run every 30 minutes. During each run, I want to check a directory for the presence of at least one .RAW file. Upon finding that file extension, I will have other code to execute, but right now I'm having trouble figuring out the correct if statement to use.

I know this code example isn't the most ideal way to do it, but here's the code I tried which is obviously wrong:

cd ./dir/
if [ ! -f "*.RAW"]; then
  echo "No files!"
else
  echo "Yes files!"
fi

How can I correctly test for the presence of the .RAW extension?

Alex26
  • 11

4 Answers4

2

With zsh:

files=(*.RAW(DN))
if (($#files)); then
  echo Yes
else
  echo No
fi

If you're only interested in regular files (or symlinks to regular files) as your -f implies, that would be:

file=(*.RAW(ND-.))

instead.

POSIXly:

has_regfiles_by_extension() {
  for ext do
    for file in .*."$ext" ."$ext" *."$ext"; do
      [ -f "$file" ] && return
    done
    return 1
  done
}

if has_regfiles_by_extension RAW raw; then
  echo YES
else
  echo NO
fi

If you wanted to use ls instead, you could do:

if ls -Aq | grep -q '\.RAW$'; then
  echo yes
else
  echo no
fi

One of the benefits is that if the directory is not readable, you'll get an error message (instead of being silently ignored in the case of shell globs).

  • What is zsh? I apologize, I'm not very knowledgeable with shell scripting yet. I'm just using sh, and I'm getting syntax errors for unexpected '(' in the "files=(*.RAW(ND-.))" line. – Alex26 Aug 22 '15 at 17:35
  • @Alex26, zsh is one of the most advanced Unix shells. By POSIXly, I mean compatible with the standard sh syntax as specified by POSIX. Many shell implementations understand that syntax (dash, bash, ksh, yash, zsh in sh emulation...) and all modern Unix-like operating sytems have a command called sh (typically, one of the above) that can interpret it which makes it for the most portable alternative. – Stéphane Chazelas Aug 22 '15 at 17:59
0

Your example answer is almost correct. First, you need to separate the testing string ".RAW" from the closing ]:

if [ ! -f "*.RAW" ];

You can do shell globbing in this context by taking the asterisk outside of the quotes. So the finished command would be:

if [ ! -f *".RAW" ];

However, this approach isn't so useful if you want to use the .RAW files. If that's the case, I'd compare the extension of each file in the directory like so:

for file in "test/"*; do
    if [ "${file##*.}" = "RAW" ]; then
        echo "Found raw file"
    fi
done

The Bash syntax ${var##*.} is a part of the clever shell parameter expansion you can do. You can find more information on the GNU website.

raehik
  • 446
0

Thanks for all the help, although I haven't been able to get some of these to work.

I did, however, successfully implement code I found in an answer to another question! (https://unix.stackexchange.com/a/87476)

export DIR=./folder
if ls ${DIR}/*.RAW &>/dev/null
then
  echo "Yes!"
else
  echo "No!"
fi
Alex26
  • 11
  • Note that export is to export a shell variable to the environment of all the commands it runs. It makes little sense here as none of the commands you run make use of that environment variable. – Stéphane Chazelas Aug 23 '15 at 10:30
  • Note that &> is not standard sh syntax. It's only recognised by bash (the shell where it originated where other shells like csh, tcsh or zsh have >& instead) and recent versions of zsh. – Stéphane Chazelas Aug 23 '15 at 10:32
  • Leaving a variable unquoted like that has a very special meaning. It's like invoking a split+glob operator. It doesn't make sense here. – Stéphane Chazelas Aug 23 '15 at 10:33
  • ls will return false if it fails to list any of the file or directories it is passed as argument. For instance, it will return false if ${DIR}/*.RAW has no match (because in that case ${DIR}/*.RAW expands to ./folder/*.RAW and there won't be any file by that name. Or it could return false, because for instance one of the .RAW files passed to ls from the expansion (by the shell) of ${DIR}/*.RAW is a directory which you don't have read access to. – Stéphane Chazelas Aug 23 '15 at 10:36
  • Note that by default, shells exclude files whose name starts with . from the expansion of globs like *.RAW, so this approach may fail to detect RAW files. Hence the (D) zsh glob qualifier in my answer (there's no equivalent in standard sh syntax unfortunately). – Stéphane Chazelas Aug 23 '15 at 10:38
  • @StéphaneChazelas I successfully removed export from my code and switched the &> to >&. However, I'm confused as to which unquoted variable you are referring to. – Alex26 Aug 25 '15 at 17:50
  • I also want to give a bit more info: This file and my python script will be set up on a computer that will be used for the sole purpose of retrieving any recent App*.RAW, EXO*.RAW, and GPS*.RAW files (we know all the naming conventions of all files that will be returned) and placing those into the subfolder I specify in code. I just want this code to call a python script to analyze the files if and only if there were new files to retrieve. That said, any changes I have tried to make to ${DIR}/*.RAW are giving me the wrong results. – Alex26 Aug 25 '15 at 17:55
  • The ls ${DIR}/*.RAW should be ls -d -- "$DIR"/*.RAW. See here and all the links to other questions referenced in there for details. – Stéphane Chazelas Aug 26 '15 at 08:30
-1

Two Bourne Shell Solutions

With the Bourne shell (which doesn't support arrays, or shell options like nullglob or failglob) you have to work around the fact that a non-zero exit status or the glob itself will be returned if a glob isn't found. For example:

if [ -n "`ls *.RAW 2> /dev/null`" ]; then
    echo "At least one RAW file found!"
else
    echo "No RAW files found."
fi

In general, one shouldn't rely on the output of the ls command, but this is sufficient for your use case and works around the limitations of the Bourne shell. If you really want to use only shell built-ins, you have to use assignment and explicitly test for the glob itself as an error condition. For example:

files=*.RAW
if [ "$files" = "*.RAW" ]; then
    echo "No RAW files found."
else
    echo "At least one RAW file found!"
fi
CodeGnome
  • 7,820
  • No. [[ *.RAW =~ RAW ]] is a test for wether the *.RAW string is matched by the RAW regular expression (which obviously it is) – Stéphane Chazelas Aug 23 '15 at 09:06
  • [ -n "$(ls *.RAW 2> /dev/null)" ] is wrong. First, there's the problem of file names starting with -. Then, if ommits hidden files. Also, it will fail if the only non-hidden RAW file in the current directory is of type directory (or symlink to directory) and is empty (but for hidden files) or not readable. – Stéphane Chazelas Aug 23 '15 at 09:12
  • Also note that the Bourne shell never supported the $(...) syntax. More than likely, the OP is not using the Bourne shell as that shell is no longer in use nowadays (except maybe on Solaris 10 and before if you're using /bin/sh instead of the standard sh in /usr/xpg4/bin). – Stéphane Chazelas Aug 23 '15 at 09:14
  • The files=*.RAW; [ "$files" = "*.RAW" ] solution is also plain wrong. It just tests wether *.RAW is the same string as *.RAW. – Stéphane Chazelas Aug 23 '15 at 09:19
  • @StéphaneChazelas You are incorrect. files=*.RAW; [ "$files" = "*.RAW" ] works because of the way globbing works when no matches are found. In the absence of nullglob or failglob, when a glob can't be expanded, the shell returns the original glob pattern (in this case, the string *.RAW), which is indeed what the match is looking for. – CodeGnome Aug 23 '15 at 19:02
  • In files=*.RAW, there's no globbing because you're assigning to a scalar variable. (see echo "$files"). There would have been globbing with files=(*.RAW) (ksh93/zsh/bash syntax). But even then [ "$files" = "*.RAW" ] (same as [ "${files[0]}" = "*.RAW" ] in ksh/bash would have been wrong because there very well may be a file called *.RAW. – Stéphane Chazelas Aug 23 '15 at 21:46