LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Need a non-binary alternative to the "test" command (https://www.linuxquestions.org/questions/linux-newbie-8/need-a-non-binary-alternative-to-the-test-command-896950/)

UNGR 08-12-2011 07:11 AM

Need a non-binary alternative to the "test" command
 
Hi everyone!

I am currently in the very very early stages of learning how to use the bash shell as well as how to make scripts for it.

Now I have a folder with pictures in it.. They are named chronologically in the following order: YYYYMMDD, for example 20090101 is January 1st, 2009. I am writing a small script which I want to do the following:

1. Check if there are any files from January
2. If there are, make a new directory called "January" and move them in there.

Now this is a very simple script, and this is what I got for now:
Code:

#! /bin/bash

PIC_DIR=/path/to/my/pictures


if [ -e $PIC_DIR/200901* ] ; then
        mkdir $PIC_DIR/January
        mv $PIC_DIR/200901* $PIC_DIR/January
fi

This script works perfectly, as long as I only have one file that fits the criteria of having a filename of 200901*.

There is a problem with this script, though: The "test" command (the []) can only evaluate true/false statements and then exit with a 0 or a 1, accordingly. If I have several files from January 2009, it does not know which one to look at, and it exits with the error:
Code:

/path/to/the/script/script.bash: line 6: [: path/to/my/pictures/20090101: binary operator expected
So I am looking for a command that will test whether there are ANY files with the name of 200901* - not if there is a single file with the name of 200901*. Any suggestions?

Best regards,
~UNGR

P.S. I realize my writing may be a little confusing, since I am still learning this and don't fully understand it all. If you need any clarifications, please don't hesitate to ask. I welcome all help!

P.P.S. I have used these forums quite a bit, but this is my very first question. You all seem like a bunch of great guys, and I'm looking forward to ruthlessly leech on your knowledge! :)

janhe 08-12-2011 07:48 AM

It seems you've read up quite a few things about the if test in bash. So I'm going to give you a slightly advanced answer.
So if only looks at the exit status of the test program ([ is simply an alias for test, as you probably knew already).

As you probably already know, other programs also have exit statuses. if can use these the same way it uses the exit status of test.

The real problem you are facing, is that test now gets fed a commandline like this:
Code:

test -e path/to/my/pictures/20090101 path/to/my/pictures/20090102 path/to/my/pictures/20090103 path/to/my/pictures/20090114
It only expects one filename after the -e argument, so the other filenames are confusing test. It sees more than one filename, so it says it wants an operator that can handle 2 arguments (-e is an operator that can handle 1 argument).
An operator that can handle 2 arguments is a binary operator, binary refers to "has 2 sides", not "is made up of 1s an 0s".

Now the problem is understood, the solution.
ls is a command that can accept multiple filenames. If it cannot find any files, it exits with exit status 2, if it can it exits with exit status 0. So if can be used together with ls to find out if there is any number of files with that name-pattern.

so this should work:
Code:

if ls path/to/my/pictures/200901*
then
  do_something
fi

This outputs error messages and a list of your files, if applicable. You probably don't want that in your script.
Also, this is quite unreadable for people who aren't used to using if this way.
Using $?, the variable with the last command's exit status, is more common.
This suppresses the output of ls and uses $?:
Code:

ls dinge > /dev/null 2>&1
if [ $? -eq 0 ]
then
  do_something
fi

PS: -eq is a binary operator, it looks at the things on its left and on its right

UNGR 08-12-2011 08:00 AM

Thank you VERY much for your answer - very quick and very detailed!

Now I understood why it would suppress the output of ls to send it to the /dev/null, but what is the "dinge" in line 1 of your solution (seen below)? This is probably incredibly basic, but the guide/tutorial I followed did not cover this.

Quote:

Originally Posted by janhe (Post 4440780)
Code:

ls dinge > /dev/null 2>&1
if [ $? -eq 0 ]
then
  do_something
fi


Thanks for your time! It is much appreciated!

janhe 08-12-2011 08:03 AM

oops...
"dinge" is a Dutch dialect word for "stuff"

I use it a lot for temporary files and testing commands.
You should substitute it for your file name pattern, like:
Code:

ls path/to/my/pictures/200901* > /dev/null 2>&1

UNGR 08-12-2011 08:07 AM

Quote:

Originally Posted by janhe (Post 4440803)
oops...
"dinge" is a Dutch dialect word for "stuff"

I use it a lot for temporary files and testing commands.
You should substitute it for your file name pattern, like:
Code:

ls path/to/my/pictures/200901* > /dev/null 2>&1

Ah - that's what I figured :) Could find it anywhere in the man pages either :D

Also, I have a single last question: The "2>&1" - what exactly does this do? My guide used ">&2" and said it was something about redirecting to Standard Error... Would you care to elaborate on this? And bear in mind, I started learning this about four days ago, so please go real slow on me!

Thanks again for your time - it is VERY much appreciated!

janhe 08-12-2011 08:30 AM

2>&1 redirects the output that the program sends to Standard Error to Standard Output
>&2 redirects the output that the program sends to Standard Output to Standard Error

Standard Output, Input and Error explanation: http://en.wikipedia.org/wiki/Standard_streams
redirection explanation: http://en.wikipedia.org/wiki/Redirection_(computing)
guide with an even better explanation of these (and more): http://tldp.org/HOWTO/pdf/Bash-Prog-Intro-HOWTO.pdf

Most programs send their output to stdout. If an error occurs, like "file not found" they send an error message to stderr. If you are simply typing in a command on the terminal, both stdout and stderr outputs are simply shown on the screen.

If you want to save the output of a command to a file, you can do that with a command like "ls -l > file"
But you probably don't want ugly error messages mixed with your pretty program output. That's why output and error are on a separate "channel".
But sometimes you do want this. That's why the 2>&1 and the other exist. I simply put both in /dev/null. Your guide preserved both on stderr, as a sort of debugging output.

PS: If you didn't already, check out some of the howtos on The Linux Documentation Project (where the Bash Programming Howto came form).

PPS: there seems to be a convention to welcome people to LQ when responding to their first post. I always forget to do that:
Hi, welcome to LinuxQuestions.org! Enjoy your stay on these forums.

qlue 08-12-2011 05:43 PM

Quote:

Originally Posted by janhe (Post 4440803)
oops...
"dinge" is a Dutch dialect word for "stuff"

I use it a lot for temporary files and testing commands.
You should substitute it for your file name pattern, like:
Code:

ls path/to/my/pictures/200901* > /dev/null 2>&1

And here I thought it was Afrikaans! :P :lol:

janhe 08-12-2011 06:42 PM

Quote:

Originally Posted by qlue (Post 4441292)
And here I thought it was Afrikaans! :P :lol:

Who knows, maybe it is!


All times are GMT -5. The time now is 07:35 AM.