-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathget-device.sh
More file actions
executable file
·219 lines (191 loc) · 6 KB
/
get-device.sh
File metadata and controls
executable file
·219 lines (191 loc) · 6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/bin/sh
########################################################################
# get-device.sh: Resolve base block device from a mountpoint
#
# Description:
# This utility takes a mountpoint path as input and determines the
# underlying base block device (e.g. /dev/sdc). The output is a single
# device path string, making it suitable for both standalone use in
# the shell and for inclusion in larger automation scripts.
# It eliminates the need to manually inspect /proc/mounts or lsblk
# output when identifying which physical device a filesystem resides on.
#
# Features:
# - Works directly from a given mountpoint, no extra arguments required.
# - Prints the canonical base device path (/dev/*) to stdout.
# - Supports regular partitions (e.g. /dev/sdc1).
# - Supports device-mapper paths (e.g. /dev/mapper/truecrypt1, /dev/dm-0).
# - Walks the dependency chain using lsblk to return the actual root device.
# - Provides deterministic exit codes to simplify error handling in scripts.
#
# Requirements:
# - Linux, findmnt(8), lsblk(8), sed(1), head(1), tail(1), awk(1)
#
# Notes:
# - Designed for Linux systems with lsblk(8) and findmnt(8) available.
# - Can be invoked interactively for quick checks or from scripts
# that require reliable device resolution.
# - Standardized error codes (see below) allow scripts to branch
# logic depending on the type of failure.
#
# Author: id774 (More info: http://id774.net)
# Source Code: https://github.com/id774/scripts
# License: The GPL version 3, or LGPL version 3 (Dual License).
# Contact: idnanashi@gmail.com
#
# Usage:
# get-device.sh <mountpoint>
# Example:
# get-device.sh ~/mnt/disk1
# => /dev/sdc
#
# Error Conditions:
# 0. Success.
# 1. General failure.
# 2. Mountpoint or source not found.
# 3. Source is not a block device.
# 126. Required command is not executable.
# 127. Required command is not installed.
#
# Version History:
# v1.1 2025-11-09
# Remove shared fail function and inline all error handling to comply with implementation policy.
# Unify argument validation behavior.
# v1.0 2025-08-31
# Initial release.
#
########################################################################
# Display full script header information extracted from the top comment block
usage() {
awk '
BEGIN { in_header = 0 }
/^#{10,}$/ { if (!in_header) { in_header = 1; next } else exit }
in_header && /^# ?/ { print substr($0, 3) }
' "$0"
exit 0
}
# Check if the system is Linux
check_system() {
if [ "$(uname -s 2>/dev/null)" != "Linux" ]; then
echo "[ERROR] This script is intended for Linux systems only." >&2
exit 1
fi
}
# Check if required commands are available and executable
check_commands() {
for cmd in "$@"; do
cmd_path=$(command -v "$cmd" 2>/dev/null)
if [ -z "$cmd_path" ]; then
echo "[ERROR] Command '$cmd' is not installed. Please install $cmd and try again." >&2
exit 127
elif [ ! -x "$cmd_path" ]; then
echo "[ERROR] Command '$cmd' is not executable. Please check the permissions." >&2
exit 126
fi
done
}
# Validate CLI arguments and mountpoint existence
validate_args() {
if [ "$#" -ne 1 ]; then
echo "[ERROR] Exactly one mountpoint argument is required." >&2
exit 2
fi
MP="$1"
if [ -z "$MP" ]; then
echo "[ERROR] Mountpoint argument is empty." >&2
exit 2
fi
if [ ! -e "$MP" ]; then
echo "[ERROR] Mountpoint does not exist: $MP" >&2
exit 2
fi
MOUNTPOINT="$MP"
return 0
}
# Resolve source device from a mountpoint
resolve_source() {
MP="$1"
[ -e "$MP" ] || return 1
SRC=$(findmnt -no SOURCE -- "$MP" 2>/dev/null || true)
[ -n "$SRC" ] || return 1
printf '%s\n' "$SRC"
return 0
}
# Convert a device path to its base disk device
to_base_disk() {
DEVPATH="$1"
[ -e "$DEVPATH" ] || return 1
case "$DEVPATH" in
/dev/mapper/*|/dev/dm-*)
# Walk full dependency chain to the root device (raw, no tree glyphs)
BASE=$(lsblk -rnp -o NAME -s -- "$DEVPATH" 2>/dev/null | tail -n 1)
[ -n "$BASE" ] || return 1
printf '%s\n' "$BASE"
return 0
;;
*)
# Prefer PKNAME for regular partitions
PK=$(lsblk -no PKNAME -- "$DEVPATH" 2>/dev/null | head -n 1)
if [ -n "$PK" ]; then
printf '/dev/%s\n' "$PK"
return 0
fi
;;
esac
# Fallback: strip partition suffix
case "$DEVPATH" in
/dev/nvme*|/dev/mmcblk*)
printf '%s\n' "$(echo "$DEVPATH" | sed 's/p[0-9]\+$//')"
;;
*)
printf '%s\n' "$(echo "$DEVPATH" | sed 's/[0-9]\+$//')"
;;
esac
return 0
}
# Resolve and print base device with explicit error handling
resolve_and_print() {
MP="$1"
SRC=$(resolve_source "$MP")
if [ "$?" -ne 0 ] || [ -z "$SRC" ]; then
echo "[ERROR] Mountpoint not found: $MP" >&2
exit 2
fi
case "$SRC" in
/dev/*)
;;
*)
echo "[ERROR] Not a block device source: $SRC" >&2
exit 3
;;
esac
if [ ! -e "$SRC" ]; then
echo "[ERROR] Source not found: $SRC" >&2
exit 2
fi
BASE=$(to_base_disk "$SRC")
if [ "$?" -ne 0 ] || [ -z "$BASE" ]; then
echo "[ERROR] Failed to resolve base device for $SRC" >&2
exit 2
fi
# Double-check that the resolved base is actually a block device
if [ ! -b "$BASE" ]; then
echo "[ERROR] Resolved path is not a block device: $BASE" >&2
exit 3
fi
printf '%s\n' "$BASE"
return 0
}
# Main entry point of the script
main() {
case "$1" in
-h|--help|-v|--version) usage ;;
esac
check_system
check_commands lsblk findmnt tail sed head
validate_args "$@"
resolve_and_print "$MOUNTPOINT"
return $?
}
# Execute main function
main "$@"