Skip to content

Commit 144124a

Browse files
committed
Added unified_extractor
1 parent 7d9a021 commit 144124a

2 files changed

Lines changed: 241 additions & 0 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
#
3+
# A Small script to add unified_extractor.sh to /usr/local/bin/ as "extract",
4+
# so that it could be used as a built-in like command.
5+
#
6+
set -euo pipefail
7+
read -t 30 -p \
8+
"Are you sure you want to add 'extract' to usr/local/bin? (Y/N):" \
9+
-n 1 -r
10+
echo
11+
[[ ! "${REPLY}" =~ ^[Yy]$ ]] && exit 1
12+
chmod +x unified_extractor.sh
13+
sudo cp unified_extractor.sh /usr/local/bin/extract
14+
echo "Completed"
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Unified archive extractor for Bash
4+
# Created by OperaVaria
5+
# lcs_it@proton.me
6+
#
7+
# Part of the "useful-bash-scripts" project:
8+
# https://github.com/OperaVaria/useful-bash-scripts
9+
# Version 1.0.0
10+
#
11+
# The script unifies the different archive extracting commands to a single one
12+
# for convenient use. Best employed aliased to "extract" or added to PATH.
13+
#
14+
# Tested on: CachyOS (rolling), GNU bash, 5.3.9(1)-release
15+
#
16+
# License:
17+
# Copyright © 2026, OperaVaria
18+
#
19+
# This program is free software: you can redistribute it and/or modify it under
20+
# the terms of the GNU General Public License as published by the Free Software
21+
# Foundation, either version 3 of the License, or (at your option) any later
22+
# version.
23+
#
24+
# This program is distributed in the hope that it will be useful, but WITHOUT
25+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
26+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
27+
# details.
28+
#
29+
# You should have received a copy of the GNU General Public License along with
30+
# this program. If not, see <https://www.gnu.org/licenses/>
31+
32+
# Exit on error, undefined variables, and pipe failures.
33+
set -euo pipefail
34+
35+
#######################################
36+
# Function to handle and validate run options.
37+
# Arguments:
38+
# Script positional arguments.
39+
# Returns:
40+
# Exit status.
41+
#######################################
42+
set_args() {
43+
local -a positional=()
44+
while [[ $# -gt 0 ]]; do
45+
case "$1" in
46+
-v|--verbose)
47+
verbose=1
48+
shift
49+
;;
50+
-h|--help)
51+
show_help
52+
exit 0
53+
;;
54+
-*)
55+
echo "❌ Unknown option '$1'"
56+
return 1
57+
;;
58+
*)
59+
positional+=("$1")
60+
shift
61+
;;
62+
esac
63+
done
64+
if [[ ${#positional[@]} -eq 0 ]]; then
65+
echo "❌ No archive specified"
66+
return 1
67+
else
68+
file="${positional[0]}"
69+
[[ ! -f "${file}" ]] \
70+
&& { echo "❌ '${file}' is not a file"; return 1; }
71+
file="$(realpath "${file}")"
72+
fi
73+
if [[ ${#positional[@]} -eq 1 ]]; then
74+
crt_dir
75+
else
76+
destination="${positional[1]}"
77+
mkdir -p "${destination}" || return 1
78+
destination="$(realpath "${destination}")"
79+
fi
80+
return 0
81+
}
82+
83+
#######################################
84+
# Displays help message.
85+
#######################################
86+
show_help() {
87+
cat << EOF
88+
Usage: standalone: ./unified_extractor.sh [OPTIONS] <archive> <destination>
89+
command: extract [OPTIONS] <archive> <destination>
90+
91+
Extracts different types of archive files with a single command.
92+
93+
ARGUMENTS:
94+
archive Archive filename
95+
destination Directory to extract to
96+
(default: new directory in the location of the
97+
archive file with the name of the archive)
98+
99+
OPTIONS:
100+
-h, --help Show this help message
101+
-v, --verbose Display (more) information prompts during the
102+
extraction process
103+
EOF
104+
}
105+
106+
#######################################
107+
# Detects file mimetype and runs proper
108+
# extracting binary.
109+
# Returns:
110+
# Exit status.
111+
#######################################
112+
run_mime() {
113+
local args mime
114+
mime=$(file --mime-type -b "${file}")
115+
case "${mime}" in
116+
application/zip)
117+
chk_cmd unzip
118+
[[ "${verbose}" -eq 1 ]] && args="-v" || args="-qq"
119+
unzip "${args}" "${file}" -d "${destination}"
120+
;;
121+
application/x-tar|application/x-gtar)
122+
chk_cmd tar
123+
[[ "${verbose}" -eq 1 ]] && args="-xvf" || args="-xf"
124+
tar "${args}" "${file}" -C "${destination}"
125+
;;
126+
application/gzip|application/x-gzip)
127+
chk_cmd tar
128+
[[ "${verbose}" -eq 1 ]] && args="-xvzf" || args="-xzf"
129+
tar "${args}" "${file}" -C "${destination}"
130+
;;
131+
application/x-bzip2)
132+
chk_cmd tar
133+
[[ "${verbose}" -eq 1 ]] && args="-xvjf" || args="-xjf"
134+
tar "${args}" "${file}" -C "${destination}"
135+
;;
136+
application/x-xz)
137+
chk_cmd tar
138+
[[ "${verbose}" -eq 1 ]] && args="-xvJf" || args="-xJf"
139+
tar "${args}" "${file}" -C "${destination}"
140+
;;
141+
application/zstd)
142+
chk_cmd tar
143+
[[ "${verbose}" -eq 1 ]] && args="-xvf" || args="-xf"
144+
tar --zstd "${args}" "${file}" -C "${destination}"
145+
;;
146+
application/x-7z-compressed)
147+
chk_cmd 7z
148+
if [[ "${verbose}" -eq 1 ]]; then
149+
7z x "${file}" -o"${destination}"
150+
else
151+
7z x "${file}" -o"${destination}" -bso0 -bsp0
152+
fi
153+
;;
154+
application/vnd.rar|application/x-rar)
155+
chk_cmd unrar
156+
if [[ "${verbose}" -eq 1 ]]; then
157+
unrar x "${file}" "${destination}/"
158+
else
159+
unrar x -inul "${file}" "${destination}/"
160+
fi
161+
;;
162+
*)
163+
echo "❌ Unsupported archive type (${mime})"
164+
return 1
165+
;;
166+
esac
167+
return 0
168+
}
169+
170+
#######################################
171+
# Check if command is available on the
172+
# system.
173+
# Arguments:
174+
# Command to be checked.
175+
# Returns:
176+
# Exit status.
177+
#######################################
178+
chk_cmd() {
179+
command -v "$1" >/dev/null 2>&1 \
180+
|| { echo "❌ Required command '$1' not found"; return 1; }
181+
return 0
182+
}
183+
184+
#######################################
185+
# Create directory path from archive
186+
# path by removing extension. Handles
187+
# *.tar.* files.
188+
# Prompts if already exists.
189+
# Returns:
190+
# Exit status.
191+
#######################################
192+
crt_dir() {
193+
if [[ "${file}" == *".tar."* ]];then
194+
destination="${file%.tar.*}"
195+
else
196+
destination="${file%.*}"
197+
fi
198+
if [[ -e "${destination}" ]]; then
199+
echo "⚠️ Directory '$(basename "${destination}")' already exists"
200+
read -t 30 -p "Continue anyway? (Y/N): " -n 1 -r
201+
echo
202+
[[ ! "${REPLY}" =~ ^[Yy]$ ]] && return 1
203+
fi
204+
mkdir -p "${destination}" || return 1
205+
return 0
206+
}
207+
208+
#######################################
209+
# Launches main function.
210+
# Arguments:
211+
# Script positional arguments.
212+
# Returns:
213+
# Exit status.
214+
#######################################
215+
extract() {
216+
local destination file
217+
local -i verbose=0
218+
set_args "$@"
219+
[[ $verbose -eq 1 ]] \
220+
&& echo -e "Extracting: '${file}'\ninto '${destination}/'"
221+
run_mime
222+
echo "✅ Completed successfully"
223+
return 0
224+
}
225+
226+
# Launch main function.
227+
extract "$@"

0 commit comments

Comments
 (0)