Pipe the output of a command if it is successful
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)
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")
fi
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):
first_file_name=($MY_DIR/FILE.*.xml(Om[1]:t))
If there's no match, that command will fail and abort the script. You can also do:
first_file_name=($MY_DIR/FILE.*.xml(NOm[1]:t))
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
else
echo >&2 No match
fi
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
done
[[ $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:
check_dir=/path/to/check
check_ext=.xml
if in_file=$(latest "$check_dir" "$check_ext"); then
base_fn=${in_file##*/} base_fn=${base_fn%.*}
else
printf '%s\n' "No file found in $check_dir" >&2
fi
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.