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.
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.
– mikeserv
Aug 15 '14 at 20:11
{ 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"
if
statements can't be used? Can you use other conditional operators such as||
? – jordanm Aug 14 '14 at 02:33ls -lrt /dir1/dir2/filename*.txt | tail -1 | awk '{print $9}' | read variable || echo "exiting..."; exit
but it doesn't work – area51 Aug 14 '14 at 02:36