36

Given the script below, I would like to avoid the execution of the pipeline if a file does not exist.

How may I exit the script straight away if the text file is not found?

ls */*.txt | grep ab1 | awk '{print $1, $1}' | sed "s/\"/\"\"/g" | xargs -L1 mv
informatik01
  • 16,038
  • 10
  • 74
  • 104
amine
  • 449
  • 1
  • 5
  • 12

4 Answers4

65

You can check for file existence with something like:

if [[ -f x.txt ]] ; then
    echo file exists.
fi

To exit if it doesn't, something like this would suffice:

if [[ ! -f x.txt ]] ; then
    echo 'File "x.txt" is not there, aborting.'
    exit
fi

The -f <file> is only one of the many conditional expressions you can use. If you look at the bash man-page under CONDITIONAL EXPRESSIONS, you'll see a whole host of them.


If (as stated in a question update) you wish to check if a wildcard results in files, you can simply expand it, throwing away the errors. If there are none, you'll end up with an empty string which can be detected with -z:

if [[ -z "$(ls -1 */*.txt 2>/dev/null | grep ab1)" ]] ; then
    echo 'There are no "*/*.txt" files.'
    exit
fi

Note that I've used -1 to force one file per line even though Linux ls does that by default if the output device is not a terminal (from memory). That's just in case you try this on a machine that doesn't force one per line in that case.


Keep in mind however that, if you have spaces in your filenames, using ls and then awk to extract column 1 is not going to work too well. For example, the file abc ab1.txt will result in the extraction of only the abc bit.

Using find with -print0, combined with xargs with -0 is the usual way to properly process files which may have "special" characters in them. There are many other options you can give to find to ensure only the files required are processed, such as -maxdepth to limit how far down the directory tree you go, and -name to properly filter file names.

However, if you know that you will never have these types of files, it's probably okay to use the ls solution, just make sure you're comfortable with its shortcomings.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
20

Use test

A quick and short way of testing for existence:

test -e $FILENAME || exit

You can chain it by doing:

test -e $FILENAME && something | something-else 

In which case something and something-else will only execute if the file exists.

For example:

➤ test -e ~/.bashrc && echo True || echo False
True
➤ test -e ./exists-not && echo True || echo False
False

Aside: Note that [ is an alias for test. The semantics of [[ are defined by bash and can be found on the bash manpage

However...

...you are probably better off using find to get your files:

find ./ -maxdepth 2 -name "*ab1*.txt" -exec do-something-to-filename '{}' \;

As the output of ls can not be reliably parsed. This will also only execute the command do-something-to-filename if the filename exist, which is what I think you are trying to do.

{} will be replaced by the filename for each invocation of do-something-to-filename.

brice
  • 24,329
  • 7
  • 79
  • 95
  • Hi brice, thanks for the alternative, "test" command is fast and simple. But what can i do if do not know the full filename and i just know the file is in .txt format. Wondering whether test command has any function to do it? – amine May 30 '13 at 02:18
  • test command doesn't and your best bet is definitely using `find`. You can use find inside of test to have everything on one line though: `test -n "$(find ./ -maxdepth 2 -name "*er*.py")" && do-stuff || handle-failure` – brice May 30 '13 at 02:23
0

I tried this and this worked for me

test -s <<your file path>> && exit 0 || <<things to do when file NOT exists>>

Note that i am actually exiting when the file is not present

Kishan B
  • 4,731
  • 1
  • 19
  • 11
-1

find ... -exec ... or find ... -print0 | xargs -0 ... is generally fine, but you can do it directly with a glob using bash's nullglob option:

bash-3.2$ echo *foo
*foo
bash-3.2$ shopt -s nullglob
bash-3.2$ echo *foo

bash-3.2$ 

Depending on the rest of your script, you may or may not want to re-unset that with shopt -u nullglob.

Kevin
  • 53,822
  • 15
  • 101
  • 132