I am writing this script that have this line:
ls -lrt /dir1/dir2/filename*.txt | tail -1 | awk '{print $9}' | read variable
What I wanted is to exit the script (without using if-statement) if it does not find any match.
This particular task doesn't call for a pipe.
In zsh:
a=(/dir1/dir2/filename*.txt(Nom[1]))
if ((! #a)); then
echo >&2 "No file matches /dir1/dir2/filename*.txt"
exit 2
fi
variable=$a[1]
or, to exit the script automatically if the glob doesn't match:
set -e
a=(/dir1/dir2/filename*.txt(om[1]))
variable=$a[1]
Other shells don't have a built-in feature to find the most recent file. Calling ls is reasonable provided that your file names only contain printable characters other than newlines, but don't pass the -l option and then parse out all but the name, that's gratuitously complex and fragile (breaks on spaces, in particular). Also, take the first match of the -t sort, that's faster than taking the last match of the -tr sort. The straightforward approach:
variable=$(ls -td /dir1/dir2/filename*.txt 2>/dev/null | head -n 1)
if [ -z "$variable" ]; then
echo >&2 "No file matches /dir1/dir2/filename*.txt"
exit 2
fi
If you want to abort your script when the left-hand side of a pipeline fails (i.e. exits with a nonzero status or due to a signal), in ksh93 and bash, you can set the pipefail option and exit if the status of the pipeline is nonzero.
set -e -o pipefail
somecommand | filter
echo "somecommand succeeded"
or
set -o pipefail
if ! somecommand | filter; then
echo >&2 "somecommand or filter failed"
exit 2
fi
Zsh doesn't have a pipefail option, but you can retrieve the status code of each component of the pipeline in the pipestatus array.
somecommand | filter
if ((pipestatus[1])); then
echo >&2 "somecommand failed"
exit 2
fi
In other shells, the status of the pipeline is the status of the right-hand command. There's no direct way to retrieve the status of the left-hand command. See Get exit status of process that's piped to another for some possible workarounds.
Beware that filter should read the whole output of somecommand, otherwise you need to handle the case when somecommand dies of a SIGPIPE.
If you want to act based on whether somecommand produces any output, rather than based on its exit status, you can use ifne from Joey Hess's moreutils. Note that most systems don't have these utilities installed by default.
touch 2 ; sleep 1 ; touch 1; for sh in mksh dash yash posh bash zsh 'busybox ash'; do command -p $sh -c '[ 1 -nt 2 ] || echo $0'; done 2>/dev/null prints only posh. So it might be more correct to say posh doesn't have a built-in feature to find the most recent file. And the most direct way to handle the return status of a pipe's left-hand command is to directly handle the return status of a pipe's left-hand command, signalling the parent shell as necessary - this is true in any shell.
{ ls -lrt /dir1/dir2/filename*.txt ||
kill $$
} | tail -1 |
awk '{print $9}' |
read variable
Just kill yourself. Sometimes the shell can be a little whiny when I do it, but I find it doesnt even whimper if I do kill -PIPE $$.
Or if you would prefer to be specific about return codes and the rest:
trap 'echo some error >&2; exit 1' USR1 $and_or_other_signals
{ ls ... || kill -USR1 $$; } |...
Regarding the rest.. Maybe another way..?
touch ~/"a n\$ew file
in 'my home
directory"
v=$(ls -1rdt ~/* || kill -PIPE $$; echo /)
v=${v%?/*}; v=${v##*/}
printf '<%s>' ~/"$v"
</home/mikeserv/a n$ew file
in 'my home
directory>
That will get you the name of the last modified file in any directory regardless of whatever characters it may contain. I use the < > to denote the begining and end of the variable and show that it does not contain any trailing white space. And the echo bit is to ensure that if there is trailing whitespace it persists.
It definitely beats the pipeline and it will still kill a scripted shell if ls returns other than 0 - such as when its command-line arguments do not exist.
The same can be done without reversing the sort order, but it is a little trickier, and it requires changing into the target directory to do it at all easily:
touch ~/"a n\$ew file
in 'my home
directory"
v=$(cd ~; ls -dtm ./* || kill -PIPE $$)
v=${v#*/}; v=${v%%,?./*}
printf '<%s>' ~/"$v"
</home/mikeserv/a n$ew file
in 'my home
directory>
Another fairly portable method, I think:
set /[d]ir1/dir2/filename*.txt
[ -z "${1##/\[*}" ] && exit 1
while [ -n "$2" ]
do [ "$1" -nt "$2" ] &&
set "$@" "$1"
shift; done; v=$1
printf %s\\n "$1" "$v"
ifstatements can't be used? Can you use other conditional operators such as||?ls -lrt /dir1/dir2/filename*.txt | tail -1 | awk '{print $9}' | read variable || echo "exiting..."; exitbut it doesn't work