Skip to content

Commit 15047b0

Browse files
committed
refactor/robust(ap/rp): improve robustness 💪 ; check failure and reflect as exit code; improve portability(portableRelPath/portableReadLink)
NOTE: - use `printf` 💪 instead of `echo`; use `if-else` instead of `&&-||` - the `echo` option(e.g. -e -n) may effect correctness, `printf` is more robust 💪 - about `&&-||` see shell check: https://www.shellcheck.net/wiki/SC2015
1 parent e9f69f7 commit 15047b0

File tree

2 files changed

+100
-44
lines changed

2 files changed

+100
-44
lines changed

bin/ap

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,24 @@ readonly PROG_VERSION='2.5.0-dev'
2121
# util functions
2222
################################################################################
2323

24-
# NOTE: $'foo' is the escape sequence syntax of bash
25-
readonly ec=$'\033' # escape char
26-
readonly eend=$'\033[0m' # escape end
27-
readonly nl=$'\n' # new line
28-
29-
colorEcho() {
24+
colorPrint() {
3025
local color="$1"
3126
shift
3227
# check isatty in bash https://stackoverflow.com/questions/10022323
3328
# if stdout is console, turn on color output.
34-
[ -t 1 ] && echo "${ec}[1;${color}m$*${eend}" || echo "$*"
29+
if [ -t 1 ]; then
30+
printf "\033[1;${color}m%s\033[0m\n" "$*"
31+
else
32+
printf '%s\n' "$*"
33+
fi
3534
}
3635

37-
redEcho() {
38-
colorEcho 31 "$@"
36+
redPrint() {
37+
colorPrint 31 "$*"
3938
}
4039

4140
die() {
42-
redEcho "Error: $*" 1>&2
41+
redPrint "Error: $*" >&2
4342
exit 1
4443
}
4544

@@ -54,10 +53,15 @@ portableReadLink() {
5453
readlink -f "$file"
5554
;;
5655
Darwin*)
56+
local py_args=(-c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$file")
5757
if command -v greadlink >/dev/null; then
5858
greadlink -f "$file"
59+
elif command -v python3 >/dev/null; then
60+
python3 "${py_args[@]}"
61+
elif command -v python >/dev/null; then
62+
python "${py_args[@]}"
5963
else
60-
python -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$file"
64+
die "fail to find command(greadlink/python3/python) to get absolute path!"
6165
fi
6266
;;
6367
*)
@@ -72,10 +76,12 @@ usage() {
7276
# shellcheck disable=SC2015
7377
[ "$exit_code" != 0 ] && local -r out=/dev/stderr || local -r out=/dev/stdout
7478

75-
(($# > 0)) && redEcho "$*$nl" >$out
79+
# NOTE: $'foo' is the escape sequence syntax of bash
80+
local nl=$'\n' # new line
81+
(($# > 0)) && redPrint "$*$nl" >$out
7682

7783
cat >$out <<EOF
78-
Usage: ${PROG} [OPTION]... ARG...
84+
Usage: ${PROG} [OPTION]... [FILE]...
7985
convert to Absolute Path.
8086
8187
Example:
@@ -91,7 +97,7 @@ EOF
9197
}
9298

9399
progVersion() {
94-
echo "$PROG $PROG_VERSION"
100+
printf '%s\n' "$PROG $PROG_VERSION"
95101
exit
96102
}
97103

@@ -115,7 +121,6 @@ while [ $# -gt 0 ]; do
115121
;;
116122
-*)
117123
usage 2 "${PROG}: unrecognized option '$1'"
118-
break
119124
;;
120125
*)
121126
# if not option, treat all follow files as args
@@ -128,10 +133,20 @@ done
128133
[ ${#files[@]} -eq 0 ] && files=(.)
129134
readonly files
130135

136+
################################################################################
137+
# biz logic
138+
################################################################################
139+
140+
has_error=false
141+
131142
for f in "${files[@]}"; do
132-
! [ -e "$f" ] && {
133-
echo "$f does not exists!"
134-
continue
135-
}
136-
portableReadLink "$f"
143+
if [ -e "$f" ]; then
144+
portableReadLink "$f"
145+
else
146+
redPrint "error: $f does not exists!" >&2
147+
has_error=true
148+
fi
137149
done
150+
151+
# set exit status
152+
! $has_error

bin/rp

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,51 @@ readonly PROG_VERSION='2.5.0-dev'
2121
# util functions
2222
################################################################################
2323

24-
# NOTE: $'foo' is the escape sequence syntax of bash
25-
readonly ec=$'\033' # escape char
26-
readonly eend=$'\033[0m' # escape end
27-
readonly nl=$'\n' # new line
28-
29-
colorEcho() {
24+
colorPrint() {
3025
local color="$1"
3126
shift
3227
# check isatty in bash https://stackoverflow.com/questions/10022323
3328
# if stdout is console, turn on color output.
34-
[ -t 1 ] && echo "${ec}[1;${color}m$*${eend}" || echo "$*"
29+
if [ -t 1 ]; then
30+
printf "\033[1;${color}m%s\033[0m\n" "$*"
31+
else
32+
printf '%s\n' "$*"
33+
fi
34+
}
35+
36+
redPrint() {
37+
colorPrint 31 "$@"
3538
}
3639

37-
redEcho() {
38-
colorEcho 31 "$@"
40+
die() {
41+
redPrint "Error: $*" >&2
42+
exit 1
43+
}
44+
45+
portableRelPath() {
46+
local file="$1" relTo="$2" uname
47+
48+
uname="$(uname)"
49+
case "$uname" in
50+
Linux* | CYGWIN* | MINGW*)
51+
realpath "$f" --relative-to="$relTo"
52+
;;
53+
Darwin*)
54+
local py_args=(-c 'import os, sys; print(os.path.relpath(sys.argv[1], sys.argv[2]))' "$file" "$relTo")
55+
if command -v grealpath >/dev/null; then
56+
grealpath "$f" --relative-to="$relTo"
57+
elif command -v python3 >/dev/null; then
58+
python3 "${py_args[@]}"
59+
elif command -v python >/dev/null; then
60+
python "${py_args[@]}"
61+
else
62+
die "fail to find command(grealpath/python3/python) to get relative path!"
63+
fi
64+
;;
65+
*)
66+
die "NOT support uname($uname)!"
67+
;;
68+
esac
3969
}
4070

4171
usage() {
@@ -44,15 +74,18 @@ usage() {
4474
# shellcheck disable=SC2015
4575
[ "$exit_code" != 0 ] && local -r out=/dev/stderr || local -r out=/dev/stdout
4676

47-
(($# > 0)) && redEcho "$*$nl" >$out
77+
# NOTE: $'foo' is the escape sequence syntax of bash
78+
local nl=$'\n' # new line
79+
(($# > 0)) && redPrint "$*$nl" >$out
4880

4981
cat >$out <<EOF
50-
Usage: ${PROG} [OPTION]... ARG...
82+
Usage: ${PROG} [OPTION]... [FILE]...
5183
convert to Relative Path.
5284
5385
Example:
54-
${PROG} arg1 arg2
55-
${PROG} */*.py
86+
${PROG} path # relative to current dir
87+
${PROG} path1 relativeToPath
88+
${PROG} */*.c relativeToPath
5689
5790
Options:
5891
-h, --help display this help and exit
@@ -63,7 +96,7 @@ EOF
6396
}
6497

6598
progVersion() {
66-
echo "$PROG $PROG_VERSION"
99+
printf '%s\n' "$PROG $PROG_VERSION"
67100
exit
68101
}
69102

@@ -87,7 +120,6 @@ while [ $# -gt 0 ]; do
87120
;;
88121
-*)
89122
usage 2 "${PROG}: unrecognized option '$1'"
90-
break
91123
;;
92124
*)
93125
# if not option, treat all follow files as args
@@ -97,10 +129,7 @@ while [ $# -gt 0 ]; do
97129
esac
98130
done
99131

100-
[ "${#files[@]}" -eq 0 ] && {
101-
redEcho "Error: NO argument!" 1>&2
102-
exit 1
103-
}
132+
[ "${#files[@]}" -eq 0 ] && die "NO argument!"
104133

105134
if [ "${#files[@]}" -eq 1 ]; then
106135
relativeTo=.
@@ -113,12 +142,24 @@ else
113142
fi
114143

115144
[ -f "$relativeTo" ] && relativeTo="$(dirname "$relativeTo")"
145+
[ -e "$relativeTo" ] || die "relativeTo dir($relativeTo) does NOT exists!"
146+
116147
readonly files relativeTo
117148

149+
################################################################################
150+
# biz logic
151+
################################################################################
152+
153+
has_error=false
154+
118155
for f in "${files[@]}"; do
119-
! [ -e "$f" ] && {
120-
echo "$f does not exists!"
121-
continue
122-
}
123-
realpath "$f" --relative-to="$relativeTo"
156+
if [ -e "$f" ]; then
157+
portableRelPath "$f" "$relativeTo"
158+
else
159+
redPrint "error: $f does not exists!" >&2
160+
has_error=true
161+
fi
124162
done
163+
164+
# set exit status
165+
! $has_error

0 commit comments

Comments
 (0)