Trabajar con archivos comprimidos en Linux obliga a recordar comandos diferentes para ZIP, TAR.GZ, XZ, Zstandard, 7-Zip, RAR o paquetes RPM y DEB. Dos pequeños scripts de Bash pueden reducir todo ese repertorio a dos órdenes: extract para extraer archivos y compress para crear copias comprimidas con nombres fechados, validación y control del formato.
Las claves de estos scripts para Linux en 30 segundos
extractreconoce los formatos más habituales y procesa varios archivos en una sola ejecución.- Cada contenido se extrae en un directorio separado para evitar sobrescrituras accidentales.
- El script detecta herramientas ausentes y rechaza rutas potencialmente peligrosas en TAR y ZIP.
compresspermite elegir entre TAR, Gzip, Bzip2, XZ, Zstandard y ZIP.- El archivo se crea en una ubicación temporal y se valida antes de trasladarlo al destino definitivo.
La idea inicial es sencilla, pero merece algunas protecciones adicionales. Ejecutar directamente gunzip archivo.gz, por ejemplo, sustituye normalmente el fichero comprimido por su contenido. Extraer varios archivos sobre el directorio actual también puede mezclar carpetas, reemplazar ficheros existentes o dejar una extracción incompleta cuando el proceso falla.
Las versiones propuestas conservan los originales, utilizan directorios temporales y no publican el resultado hasta que la operación ha terminado correctamente. También muestran un mensaje claro cuando falta unzip, 7z, unrar, zstd, dpkg-deb u otra dependencia.
GNU Tar puede crear y leer archivos comprimidos con Gzip, Bzip2, XZ y Zstandard, entre otros formatos. Al extraer desde un fichero, puede reconocer automáticamente la compresión mediante la firma interna y, si es necesario, por la extensión.
Un comando para extraer TAR, ZIP, 7Z, RAR, DEB y RPM
El primer script acepta uno o varios archivos. En lugar de volcar todo en el directorio desde el que se ejecuta, crea una carpeta independiente para cada elemento.
Por ejemplo, backup.tar.gz se extraerá dentro de backup/. Si esa carpeta ya existe, el script generará otro nombre con fecha y un contador.
Guárdelo como extract:
#!/usr/bin/env bash
# extract — extractor universal y prudente para Linux
# Uso: extract [opciones] <archivo> [archivo ...]
# Opciones:
# -d, --directory DIR Directorio raíz de salida (por defecto: .)
# -l, --list Mostrar el contenido sin extraer
# -v, --verbose Mostrar los archivos procesados
# -h, --help Mostrar ayuda
set -u -o pipefail
ROOT="."
LIST_ONLY=0
VERBOSE=0
usage() {
sed -n '2,9p' "$0" | sed 's/^# \{0,1\}//'
}
error() {
printf 'extract: %s\n' "$*" >&2
}
need() {
command -v "$1" >/dev/null 2>&1 || {
error "falta la herramienta '$1'"
return 127
}
}
archive_stem() {
local name=${1##*/}
local lower=${name,,}
case "$lower" in
*.tar.gz)
printf '%s\n' "${name:0:${#name}-7}"
;;
*.tar.bz2)
printf '%s\n' "${name:0:${#name}-8}"
;;
*.tar.xz)
printf '%s\n' "${name:0:${#name}-7}"
;;
*.tar.zst)
printf '%s\n' "${name:0:${#name}-8}"
;;
*.tbz2)
printf '%s\n' "${name:0:${#name}-5}"
;;
*.tgz|*.txz)
printf '%s\n' "${name:0:${#name}-4}"
;;
*.zip|*.rar|*.deb|*.rpm|*.tar)
printf '%s\n' "${name%.*}"
;;
*.7z|*.gz|*.bz2|*.xz|*.zst)
printf '%s\n' "${name%.*}"
;;
*.z)
printf '%s\n' "${name:0:${#name}-2}"
;;
*)
printf '%s\n' "$name"
;;
esac
}
unique_destination() {
local base="$ROOT/$(archive_stem "$1")"
local candidate=$base
local n=1
while [[ -e "$candidate" ]]; do
candidate="${base}_$(date +%Y%m%d_%H%M%S)_$n"
((n++))
done
printf '%s\n' "$candidate"
}
check_member_paths() {
# Rechaza rutas absolutas y componentes ".." en TAR y ZIP.
awk '
{
p=$0
gsub(/\\/, "/", p)
if (
p ~ /^\// ||
p ~ /^[A-Za-z]:\// ||
p ~ /(^|\/)\.\.($|\/)/
) {
print \
"extract: ruta insegura dentro del archivo: " \
$0 > "/dev/stderr"
bad=1
}
}
END {
exit bad ? 1 : 0
}
'
}
list_archive() {
local file=$1
local lower=${file,,}
case "$lower" in
*.tar|*.tar.gz|*.tgz|*.tar.bz2|*.tbz2|\
*.tar.xz|*.txz|*.tar.zst)
need tar && tar -tf "$file"
;;
*.zip)
need unzip && unzip -Z1 "$file"
;;
*.7z)
need 7z && 7z l "$file"
;;
*.rar)
need unrar && unrar lb "$file"
;;
*.deb)
need dpkg-deb && dpkg-deb -c "$file"
;;
*.rpm)
if command -v rpm2archive >/dev/null 2>&1; then
rpm2archive "$file" | tar -tzf -
else
need rpm2cpio &&
need cpio &&
rpm2cpio "$file" | cpio -t
fi
;;
*.gz|*.bz2|*.xz|*.zst|*.z)
printf '%s\n' "$(archive_stem "$file")"
;;
*)
error "formato desconocido: '$file'"
return 2
;;
esac
}
extract_archive() {
local file=$1
local lower=${file,,}
local destination
local tmp
local output_name
destination=$(unique_destination "$file")
tmp=$(mktemp -d "$ROOT/.extract.XXXXXX") || return 1
case "$lower" in
*.tar|*.tar.gz|*.tgz|*.tar.bz2|*.tbz2|\
*.tar.xz|*.txz|*.tar.zst)
need tar || {
rm -rf -- "$tmp"
return 1
}
if ! tar -tf "$file" | check_member_paths; then
rm -rf -- "$tmp"
return 1
fi
if ((VERBOSE)); then
tar -xvf "$file" -C "$tmp"
else
tar -xf "$file" -C "$tmp"
fi
;;
*.zip)
need unzip || {
rm -rf -- "$tmp"
return 1
}
if ! unzip -Z1 "$file" | check_member_paths; then
rm -rf -- "$tmp"
return 1
fi
if ((VERBOSE)); then
unzip "$file" -d "$tmp"
else
unzip -q "$file" -d "$tmp"
fi
;;
*.7z)
need 7z || {
rm -rf -- "$tmp"
return 1
}
if ((VERBOSE)); then
7z x -y "-o$tmp" "$file"
else
7z x -y -bso0 -bsp0 "-o$tmp" "$file"
fi
;;
*.rar)
need unrar || {
rm -rf -- "$tmp"
return 1
}
if ((VERBOSE)); then
unrar x -o+ "$file" "$tmp/"
else
unrar x -idq -o+ "$file" "$tmp/"
fi
;;
*.deb)
need dpkg-deb || {
rm -rf -- "$tmp"
return 1
}
# Extrae archivos y metadatos en DEBIAN/.
dpkg-deb -R "$file" "$tmp"
;;
*.rpm)
need tar || {
rm -rf -- "$tmp"
return 1
}
if command -v rpm2archive >/dev/null 2>&1; then
rpm2archive "$file" |
tar -xzf - -C "$tmp"
else
need rpm2cpio && need cpio || {
rm -rf -- "$tmp"
return 1
}
rpm2cpio "$file" |
(
cd "$tmp" &&
cpio -idm --quiet
)
fi
;;
*.gz)
need gzip || {
rm -rf -- "$tmp"
return 1
}
output_name=$(archive_stem "$file")
gzip -dc -- "$file" >"$tmp/$output_name"
;;
*.bz2)
need bzip2 || {
rm -rf -- "$tmp"
return 1
}
output_name=$(archive_stem "$file")
bzip2 -dc -- "$file" >"$tmp/$output_name"
;;
*.xz)
need xz || {
rm -rf -- "$tmp"
return 1
}
output_name=$(archive_stem "$file")
xz -dc -- "$file" >"$tmp/$output_name"
;;
*.zst)
need zstd || {
rm -rf -- "$tmp"
return 1
}
output_name=$(archive_stem "$file")
zstd -q -dc -- "$file" >"$tmp/$output_name"
;;
*.z)
need uncompress || {
rm -rf -- "$tmp"
return 1
}
output_name=$(archive_stem "$file")
uncompress -c "$file" >"$tmp/$output_name"
;;
*)
error "formato desconocido: '$file'"
rm -rf -- "$tmp"
return 2
;;
esac
if [[ $? -ne 0 ]]; then
error "falló la extracción de '$file'"
rm -rf -- "$tmp"
return 1
fi
mv -- "$tmp" "$destination"
printf 'Extraído: %s -> %s\n' \
"$file" "$destination"
}
while (($#)); do
case "$1" in
-d|--directory)
shift
[[ $# -gt 0 ]] || {
error "falta el directorio tras --directory"
exit 2
}
ROOT=$1
;;
-l|--list)
LIST_ONLY=1
;;
-v|--verbose)
VERBOSE=1
;;
-h|--help)
usage
exit 0
;;
--)
shift
break
;;
-*)
error "opción desconocida: '$1'"
usage >&2
exit 2
;;
*)
break
;;
esac
shift
done
(($# > 0)) || {
usage >&2
exit 2
}
mkdir -p -- "$ROOT" || exit 1
if ((EUID == 0)); then
error \
"aviso: evita extraer archivos no confiables como root"
fi
status=0
for file in "$@"; do
if [[ ! -f "$file" ]]; then
error \
"no existe o no es un archivo regular: '$file'"
status=1
continue
fi
if ((LIST_ONLY)); then
printf '\n==> %s <==\n' "$file"
list_archive "$file" || status=1
else
extract_archive "$file" || status=1
fi
done
exit "$status"
Lenguaje del código: PHP (php)
La opción -l permite revisar el contenido antes de extraerlo:
extract --list backup.tar.zst
Lenguaje del código: CSS (css)
También admite varios archivos y un destino común:
extract \
--directory "$HOME/extraidos" \
copia.zip \
logs.tar.xz \
paquete.deb
Lenguaje del código: JavaScript (javascript)
Para mostrar cada fichero procesado:
extract --verbose proyecto.7z
Lenguaje del código: CSS (css)
La diferencia más importante frente a un alias basado únicamente en case es que esta versión no descomprime directamente sobre la ubicación final. Primero utiliza una carpeta oculta creada con mktemp. Si el comando falla, la carpeta temporal se elimina y no queda un resultado que parezca completo.
En TAR y ZIP también se revisan los nombres internos. El script rechaza rutas absolutas, unidades de Windows y componentes .. que podrían intentar escribir fuera del directorio seleccionado.
Esta valid los nombres internos. El script rechaza rutas absolutasación no convierte en fiable un archivo procedente de una fuente desconocida. La recomendación continúa siendo extraerlo sin privilegios de administrador, dentro de un directorio vacío y, cuando el riesgo lo justifique, en un contenedor o máquina virtual aislada.
La compatibilidad con paquetes DEB utiliza dpkg-deb -R. Esta opción extrae tanto el árbol de archivos como la información de control dentro de DEBIAN/. Debian advierte de que extraer un paquete no equivale a instalarlo: para una instalación correcta deramientas de gestión de paquetes. citeturn574853view1
Con RPM se prioriza rpm2archive. La propia documentación de RPM considera obsoleto rpm2cpio porque el formato CPIO no puede contener archivos individuales de más de 4 GB. Cuando rpm2archive no está dispcomo alternativa. citeturn574853view2turn119306view0
Un script para crear copias TAR, Gzip, XZ, Zstandard o ZIP
El segundo comando recibe un archivo o directorio y genera una copia comprimida con fecha y hora. Por defecto utiliza tar.gz, pero puede seleccionarse otro formato, ajustar el nivel de compresión y calcular un SHA-256.
Guárdelo como compress:
#!/usr/bin/env bash
# compress — crea archivos comprimidos con verificación
# Uso: compress [opciones] <archivo-o-directorio>
# Opciones:
# -f, --format FMT tar, gz, bz2, xz, zst o zip
# Formato por defecto: gz
# -o, --output FILE Archivo de salida
# -l, --level N Nivel de compresión
# -c, --checksum Mostrar SHA-256 al terminar
# -v, --verbose Mostrar los archivos añadidos
# -h, --help Mostrar ayuda
set -Eeuo pipefail
FORMAT="gz"
OUTPUT=""
LEVEL=""
CHECKSUM=0
VERBOSE=0
TMPDIR_WORK=""
usage() {
sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'
}
error() {
printf 'compress: %s\n' "$*" >&2
}
need() {
command -v "$1" >/dev/null 2>&1 || {
error "falta la herramienta '$1'"
exit 127
}
}
cleanup() {
if [[ -n "$TMPDIR_WORK" &&
-d "$TMPDIR_WORK" ]]; then
rm -rf -- "$TMPDIR_WORK"
fi
}
trap cleanup EXIT INT TERM
validate_level() {
[[ -z "$LEVEL" ]] && return 0
[[ "$LEVEL" =~ ^[0-9]+$ ]] || {
error "el nivel debe ser un número entero"
exit 2
}
case "$FORMAT" in
gz|bz2|zip)
((LEVEL >= 1 && LEVEL <= 9)) || {
error \
"para $FORMAT usa un nivel entre 1 y 9"
exit 2
}
;;
xz)
((LEVEL >= 0 && LEVEL <= 9)) || {
error "para xz usa un nivel entre 0 y 9"
exit 2
}
;;
zst)
((LEVEL >= 1 && LEVEL <= 22)) || {
error \
"para zst usa un nivel entre 1 y 22"
exit 2
}
;;
tar)
error \
"el formato tar no admite nivel de compresión"
exit 2
;;
esac
}
while (($#)); do
case "$1" in
-f|--format)
shift
[[ $# -gt 0 ]] || {
error "falta el formato"
exit 2
}
FORMAT=${1,,}
;;
-o|--output)
shift
[[ $# -gt 0 ]] || {
error "falta el archivo de salida"
exit 2
}
OUTPUT=$1
;;
-l|--level)
shift
[[ $# -gt 0 ]] || {
error "falta el nivel"
exit 2
}
LEVEL=$1
;;
-c|--checksum)
CHECKSUM=1
;;
-v|--verbose)
VERBOSE=1
;;
-h|--help)
usage
exit 0
;;
--)
shift
break
;;
-*)
error "opción desconocida: '$1'"
usage >&2
exit 2
;;
*)
break
;;
esac
shift
done
(($# == 1)) || {
usage >&2
exit 2
}
TARGET=$1
[[ -e "$TARGET" ]] || {
error "no existe: '$TARGET'"
exit 1
}
case "$FORMAT" in
tar)
EXT=".tar"
;;
gz)
EXT=".tar.gz"
;;
bz2)
EXT=".tar.bz2"
;;
xz)
EXT=".tar.xz"
;;
zst)
EXT=".tar.zst"
;;
zip)
EXT=".zip"
;;
*)
error \
"formato desconocido '$FORMAT'. " \
"Usa: tar gz bz2 xz zst zip"
exit 2
;;
esac
validate_level
need realpath
need tar
SOURCE=$(realpath -e -- "$TARGET")
[[ "$SOURCE" != "/" ]] || {
error "se rechaza comprimir la raíz completa '/'"
exit 2
}
PARENT=$(dirname -- "$SOURCE")
NAME=$(basename -- "$SOURCE")
STAMP=$(date +%Y%m%d_%H%M%S)
if [[ -z "$OUTPUT" ]]; then
OUTPUT="${NAME}_${STAMP}${EXT}"
fi
[[ ! -e "$OUTPUT" ]] || {
error "el archivo de salida ya existe: '$OUTPUT'"
exit 1
}
OUTPUT_DIR=$(dirname -- "$OUTPUT")
mkdir -p -- "$OUTPUT_DIR"
TMPDIR_WORK=$(
mktemp -d "${TMPDIR:-/tmp}/compress.XXXXXX"
)
TMPFILE="$TMPDIR_WORK/archive$EXT"
TAR_VERBOSE=()
((VERBOSE)) && TAR_VERBOSE=(-v)
case "$FORMAT" in
tar)
(
cd "$PARENT" &&
tar \
-c \
"${TAR_VERBOSE[@]}" \
-f "$TMPFILE" \
-- "$NAME"
)
;;
gz)
need gzip
GZIP_LEVEL=()
[[ -n "$LEVEL" ]] &&
GZIP_LEVEL=("-$LEVEL")
(
cd "$PARENT" &&
tar \
-c \
"${TAR_VERBOSE[@]}" \
-f - \
-- "$NAME"
) |
gzip \
-c \
"${GZIP_LEVEL[@]}" \
>"$TMPFILE"
;;
bz2)
need bzip2
BZ2_LEVEL=()
[[ -n "$LEVEL" ]] &&
BZ2_LEVEL=("-$LEVEL")
(
cd "$PARENT" &&
tar \
-c \
"${TAR_VERBOSE[@]}" \
-f - \
-- "$NAME"
) |
bzip2 \
-c \
"${BZ2_LEVEL[@]}" \
>"$TMPFILE"
;;
xz)
need xz
XZ_LEVEL=()
[[ -n "$LEVEL" ]] &&
XZ_LEVEL=("-$LEVEL")
(
cd "$PARENT" &&
tar \
-c \
"${TAR_VERBOSE[@]}" \
-f - \
-- "$NAME"
) |
xz \
-c \
-T0 \
"${XZ_LEVEL[@]}" \
>"$TMPFILE"
;;
zst)
need zstd
ZST_LEVEL=()
[[ -n "$LEVEL" ]] &&
ZST_LEVEL=("-$LEVEL")
(
cd "$PARENT" &&
tar \
-c \
"${TAR_VERBOSE[@]}" \
-f - \
-- "$NAME"
) |
zstd \
-q \
-c \
-T0 \
"${ZST_LEVEL[@]}" \
>"$TMPFILE"
;;
zip)
need zip
ZIP_LEVEL=()
[[ -n "$LEVEL" ]] &&
ZIP_LEVEL=("-$LEVEL")
if ((VERBOSE)); then
(
cd "$PARENT" &&
zip \
-r \
-y \
"${ZIP_LEVEL[@]}" \
"$TMPFILE" \
"$NAME"
)
else
(
cd "$PARENT" &&
zip \
-q \
-r \
-y \
"${ZIP_LEVEL[@]}" \
"$TMPFILE" \
"$NAME"
)
fi
;;
esac
# Comprueba que el archivo recién creado puede leerse.
if [[ "$FORMAT" == "zip" ]]; then
need unzip
unzip -tq "$TMPFILE" >/dev/null
else
tar -tf "$TMPFILE" >/dev/null
fi
mv -- "$TMPFILE" "$OUTPUT"
SIZE=$(
du -h -- "$OUTPUT" |
awk '{print $1}'
)
printf 'Creado: %s (%s)\n' \
"$OUTPUT" "$SIZE"
if ((CHECKSUM)); then
need sha256sum
sha256sum -- "$OUTPUT"
fi
Lenguaje del código: PHP (php)
El uso más sencillo crea una copia tar.gz:
compress ./mi-proyecto
El resultado tendrá un nombre similar a este:
mi-proyecto_20260715_184530.tar.gz
Lenguaje del código: CSS (css)
Para utilizar Zstandard y calcular una suma de comprobación:
compress \
--format zst \
--level 10 \
--checksum \
./copias
Para generar un ZIP con un nombre concreto:
compress \
--format zip \
--output entrega.zip \
./documentacion
Para crear un archivo XZ con mayor compresión:
compress \
--format xz \
--level 9 \
./logs
El script cambia primero al directorio padre del elemento. De esta forma, el archivo contiene mi-proyecto/ en lugar de una ruta absoluta como /home/usuario/proyectos/mi-proyecto/. Esto facilita restaurarlo en otra máquina y evita guardar innecesariamente la estructura completa del sistema.
También rechaza la raíz / como objetivo. Una orden escrita por error no debería comenzar a comprimir todo el servidor, incluidos sistemas virtuales, montajes y directorios que cambian mientras se procesa la copia.
Antes de mover el resultado al nombre solicitado, compress intenta leer su índice. En ZIP utiliza unzip -tq; en los formatos TAR ejecuta tar -tf. Esta comprobación no garantiza que cada byte pueda recuperarse tras un fallo posterior del almacenamiento, pero detecta muchos archivos vacíos, incompletos o creados por un proceso que terminó con error.
Qué formato conviene utilizar
| Formato | Ventaja práctica | Uso habitual |
|---|---|---|
tar.gz | Gran compatibilidad y proceso sencillo | Copias rápidas, código y distribución de archivos |
tar.bz2 | Compatible con sistemas antiguos | Archivos heredados que ya utilizan Bzip2 |
tar.xz | Prioriza la reducción de tamaño | Distribución y conservación de archivos |
tar.zst | Buen equilibrio para operaciones frecuentes | Backups, logs y grandes conjuntos de datos |
zip | Fácil de abrir en Linux, Windows y macOS | Intercambio con usuarios no técnicos |
tar | Agrupa sin comprimir | Datos ya comprimidos o procesamiento intermedio |
Gzip comprime flujos o archivos individuales; no agrupa por sí mismo árboles completos de directorios. Por eso el script crea primero una secuencia TAR y la envía después a Gzip, Bzip2, XZ o Zstandard. GNU Tar documenta tanto este uso mediante tuberías como la selec de la extensión. citeturn574853view0turn574853view3
Los formatos XZ y Zstandard pueden utilizar varios hilos con las opciones empleadas en el script. La elección del nivel debe considerar que una compresión más alta suele consumir más CPU y memoria. Para una copia temporal antes de actualizar un servicio, el nivel predeterminado suele resultar más razonable que utilizar siempre el máximo.
Cómo instalar los dos comandos en el sistema
Después de guardar los archivos, deben recibir permisos de ejecución:
chmod +x extract compress
Pueden ejecutarse desde el directorio actual:
./extract copia.tar.gz
./compress ./proyecto
Para convertirlos en comandos disponibles para todos los usuarios autorizados del sistema:
sudo install \
-m 0755 \
extract \
/usr/local/bin/extract
sudo install \
-m 0755 \
compress \
/usr/local/bin/compress
A partir de ese momento bastará con escribir:
extract archivo.zip
compress --format zst ./datos
No todas las distribuciones incluyen de serie las herramientas para cada formato. Los comandos básicos tar, gzip, bzip2 y xz suelen estar presentes en muchas instalaciones, mientras que zstd, 7z, unrar, zip, unzip, cpio o las utilidades RPM pueden requerir paquetes adicionales.
El propio script informa del comando ausente:
extract: falta la herramienta '7z'
Lenguaje del código: JavaScript (javascript)
Esto evita asumir nombres de paquetes que cambian entre Debian, Ubuntu, Fedora, Rocky Linux, AlmaLinux, openSUSE o Arch Linux. Una vez identificado el binario, puede buscarse el paquete correspondiente en los repositorios de la distribución.
Los scripts tampoco deberían reemplazar una estrategia de backup. Una copia comprimida guardada en el mismo disco que los datos originales puede desaparecer ante una avería, un borrado accidental o un ataque. Su utilidad está en crear entregas, snapshots rápidos, paquetes de logs y copias previas a una modificación.
Preguntas frecuentes
¿El script extract borra el archivo comprimido original?
No. Para archivos .gz, .bz2, .xz y .zst utiliza la salida estándar del descompresor y conserva el fichero original.
¿Se pueden extraer varios archivos con una sola orden?
Sí. El comando acepta todos los archivos indicados y crea un directorio separado para cada uno.
¿Por qué cada archivo se extrae en su propia carpeta?
Así se reduce el riesgo de mezclar contenidos o reemplazar ficheros existentes. Si el nombre ya está ocupado, el script crea otro con fecha y contador.
¿Cuál es el mejor formato para una copia rápida?
tar.gz ofrece una compatibilidad amplia. tar.zst resulta apropiado cuando Zstandard está instalado y se busca un proceso rápido para copias frecuentes, mientras que ZIP facilita compartir archivos con otros sistemas.





