Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top
INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml | head -1 | xargs basename`

I wanted to execute the second command (head -1) only if the first command is successful. How do I improve this command?

share|improve this question
What do you mean by successful? ls will not fail. – Matteo Jul 23 '13 at 13:12
But the glob can fail if there are no matching files. – tripleee Jul 23 '13 at 13:17
up vote 5 down vote accepted

Try this:

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml | head -1 | xargs -r basename`

Passing xargs the -r flag will cause it to only run basename if reads at least one item from standard input (head -1).

head -1 will run but you won't see or capture any output from it.

Also, if you don't want the user to see any error output from ls, you can redirect ls's stderr stream to /dev/null.

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename`

Also note that I added quotation marks around $MY_DIR. That way, the command will not fail if $MY_DIR contains spaces.

If you're using a modern shell such as bash, you should use a $( ) capture shell instead of backticks. You should also consider changing the style of your variables. You should generally avoid using all-uppercase variable names in scripts. That style is generally reserved for reserved and environmental variables.

input_file=$(ls -rt "$my_dir"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename)
share|improve this answer
Doesn't this b0rk if there are no files? – Mel Boyce Jul 23 '13 at 13:44
Actually: ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basename works. – Mel Boyce Jul 23 '13 at 13:48
@MelBoyce: I've added that to my answer. Thanks. – Evan Teitelman Jul 23 '13 at 13:51
ls is a tool for interactively looking at file information. Its output is formatted for humans and will cause bugs in scripts. Use globs or find instead. Understand why: mywiki.wooledge.org/ParsingLs – cinelli Jul 23 '13 at 15:56
@cinelli: That is true. I was just answering the question. – Evan Teitelman Jul 23 '13 at 16:09

In a pipe line, all commands are started and run concurrently, not one after the other. So you need to store the output somewhere.

if ls_output=$(ls -rtd -- "$MY_DIR"/FILE.*.xml); then
  first_file=$(printf '%s\n' "$ls_output" | head -n 1)
  first_file_name=$(basename -- "$first_file")

Note that it assumes file names don't contain newline characters. Using xargs would also mean problems with blank characters, single and double quotes and backslashes. Leaving variables unquoted would mean problems with space, tab and wildcard characters. Forgetting -- would mean problems with filenames starting with -.

To get the basename of the oldest file with zsh without any of those restrictions on characters (also avoids the problem of the limited size of arguments to a command):


If there's no match, that command will fail and abort the script. You can also do:


In which case the first_file_name array will contain 1 element if there's a match or 0 if not. You can then do:

 if (($#first_file_name)); then
    printf 'Match: %s\n' $first_file_name
    echo >&2 No match
share|improve this answer

Find the latest modified file in a directory:

latest() {
  local file path=${1:-.} ext=${2-} latest
  for file in "${path%/}"/*"$ext"; do 
    [[ $file -nt $latest ]] && latest=$file
  [[ $latest ]] && printf '%s\n' "$latest"

Usage: latest [directory/path/ [.extension]]

Instead of calling out to basename, use parameter expansion.

in_file=$(latest in/my/dir .xml)
base_fn=${in_file##*/} base_fn=${base_fn%.*}

In a directory with these contents:

foo.xml bar.xml baz.xml newest.xml

The contents of the base_fn variable would be: newest

To use this properly to serve the purpose of your request:

if in_file=$(latest "$check_dir" "$check_ext"); then
  base_fn=${in_file##*/} base_fn=${base_fn%.*}
  printf '%s\n' "No file found in $check_dir" >&2

EDIT: upon review of the question, i have realized that the ls command in question is looking for the oldest file in a directory. this same function could be renamed to oldest and have [[ $file -ot $oldest ]] && oldest=$file instead to achieve the same effect. apologies for any confusion.

the important thing to note is that you absolutely, under no circumstances ever in mankind, should not parse the output of ls. never.

share|improve this answer
Note that contrary to the ls-based approaches, if there are symlinks, the modification time of the target of the symlinks will be considered (add the -L option to ls to get the same behaviour there). – Stéphane Chazelas Jun 7 at 5:29

I think that the best option here is:

ls -rf $MY_DIR/FILE.*.xml
if [ $? -eq 0 ]; then
      INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml|head -1|xargs basename `
      echo "Error!"

if there is no file then the return code of ls is 2 but if it finds some file will be 0.

share|improve this answer
Instead of comparing $? against 0, you can do this: if ls -rf $MY_DIR/FILE.*.xml ; then. – Evan Teitelman Jul 23 '13 at 13:27
Also, you can redirect the output of the first comand to /dev/null. ls -rf $MY_DIR/FILE.*.xml &> /dev/null. That way, the user won't see the extra output. – Evan Teitelman Jul 23 '13 at 13:36
Thank you all :) – Govind Kailas Jul 25 '13 at 6:32

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.