DEV Community

Chris Watson
Chris Watson

Posted on

1

Safari-like archive handling in Linux

Just wanted to share this, because I spent all morning figuring it out for myself. As someone that's used macOS a lot recently one of the features I really appreciate is Safari's ability to automatically extract archives when you open them. Granted that's about the only feature I appreciate about Safari, but I'll give credit where credit is due.

Having recently reinstalled Arch on my home workstation this is one the the features that I've really missed, so this morning I decided to add the feature myself.

Starting off I knew I'd most likely need to write a script and somehow associate the mime-types of the various archives I want to open with that script. Keep in mind that these instructions are known to work on GNOME and require the zsh shell. Different types of archives also require different programs to open them.

Here are the 2 files you're going to need. I'll explain them a bit further down.

First is extract.zsh (place this in ~/.local/bin):

# Copyright 2021 Chris Watson (watzon)
#
# Use of this source code is governed by an MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT.
# Most of this is taken verbatim from the oh-my-zsh "extract" plugin, just with a few changes.
# Please see: https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/extract
# ~/.local/bin/extract.zsh
extract() {
setopt localoptions noautopushd
if (( $# == 0 )); then
cat >&2 <<'EOF'
Usage: extract [-option] [file ...]
Options:
-r, --remove Remove archive after unpacking.
EOF
fi
local remove_archive=1
if [[ "$1" == "-r" ]] || [[ "$1" == "--remove" ]]; then
remove_archive=0
shift
fi
local pwd="$PWD"
while (( $# > 0 )); do
if [[ ! -f "$1" ]]; then
echo "extract: '$1' is not a valid file" >&2
shift
continue
fi
local success=0
local extract_dir="${1:a:r}"
local file="$1" full_path="${1:A}"
case "${file:l}" in
(*.tar.gz|*.tgz) (( $+commands[pigz] )) && { pigz -dc "$file" | tar xv } || tar zxvf --overwrite "$file" ;;
(*.tar.bz2|*.tbz|*.tbz2) tar xvjf --overwrite "$file" ;;
(*.tar.xz|*.txz)
tar --xz --help &> /dev/null \
&& tar --xz -xvf --overwrite "$file" \
|| xzcat "$file" | tar xvf --overwrite - ;;
(*.tar.zma|*.tlz)
tar --lzma --help &> /dev/null \
&& tar --lzma -xvf --overwrite "$file" \
|| lzcat "$file" | tar xvf --overwrite - ;;
(*.tar.zst|*.tzst)
tar --zstd --help &> /dev/null \
&& tar --zstd -xvf --overwrite "$file" \
|| zstdcat "$file" | tar xvf --overwrite - ;;
(*.tar) tar xvf --overwrite "$file" ;;
(*.tar.lz) (( $+commands[lzip] )) && tar xvf --overwrite "$file" ;;
(*.tar.lz4) lz4 -c -f -d "$file" | tar xvf --overwrite - ;;
(*.tar.lrz) (( $+commands[lrzuntar] )) && lrzuntar -f "$file" ;;
(*.gz) (( $+commands[pigz] )) && pigz -dk "$file" || gunzip -f -k "$file" ;;
(*.bz2) bunzip2 -f "$file" ;;
(*.xz) unxz -f "$file" ;;
(*.lrz) (( $+commands[lrunzip] )) && lrunzip -f "$file" ;;
(*.lz4) lz4 -f -d "$file" ;;
(*.lzma) unlzma -f "$file" ;;
(*.z) uncompress -f "$file" ;;
(*.zip|*.war|*.jar|*.ear|*.sublime-package|*.ipa|*.ipsw|*.xpi|*.apk|*.aar|*.whl) unzip -o "$file" -d "$extract_dir" ;;
(*.rar) unrar x -o+ -ad "$file" ;;
(*.rpm)
command mkdir -p "$extract_dir" && builtin cd -q "$extract_dir" \
&& rpm2cpio "$full_path" | cpio --quiet -id ;;
(*.7z) 7za x -aoa "$file" ;;
(*.deb)
command mkdir -p "$extract_dir/control" "$extract_dir/data"
builtin cd -q "$extract_dir"; ar vx "$full_path" > /dev/null
builtin cd -q control; extract ../control.tar.*
builtin cd -q ../data; extract ../data.tar.*
builtin cd -q ..; command rm *.tar.* debian-binary ;;
(*.zst) unzstd -f "$file" ;;
(*.cab) cabextract -d "$extract_dir" "$file" ;;
(*.cpio) cpio -idmvF "$file" ;;
(*)
echo "extract: '$file' cannot be extracted" >&2
success=1 ;;
esac
(( success = success > 0 ? success : $? ))
(( success == 0 && remove_archive == 0 )) && rm "$full_path"
shift
# Go back to original working directory in case we ran cd previously
builtin cd -q "$pwd"
done
}
extract $1
xdg-open ${1:a:r}
view raw extract.zsh hosted with ❤ by GitHub

And then the desktop file (place this in ~/.local/share/applications:

[Desktop Entry]
Comment=Extract archives regardless of type
Terminal=true
Name=extract
Exec=/usr/bin/env zsh ~/.local/bin/extract.zsh %f
Type=Application
Icon=/usr/share/icons/gnome/48x48/apps/gnome-settings-theme.png
Encoding=UTF-8
Hidden=false
NoDisplay=false
Categories=AudioVideo;Player;Recorder;
MimeType=application/gzip;application/x-7z-compressed;application/x-7z-compressed-tar;application/x-arj;application/x-bzip;application/x-bzip-compressed-tar;application/x-bzip1;application/x-bzip1-compressed-tar;application/x-cabinet;application/x-cd-image;application/x-compress;application/x-compressed-tar;application/x-cpio;application/x-deb;application/x-gtar;application/x-gzip;application/x-gzpostscript;application/x-lha;application/x-lhz;application/x-lzma;application/x-lzma-compressed-tar;application/x-ms-wim;application/x-rar;application/x-rar-compressed;application/x-rpm;application/x-tar;application/x-xz;application/x-xz-compressed-tar;application/x-zip;application/x-zip-compressed;application/zip;application/vnd.ms-cab-compressed;
view raw extract.desktop hosted with ❤ by GitHub

How does it work?

Honestly it's super simple, and makes use of some existing code from Oh-My-ZSH. extract.zsh checks the extension of the file you're trying to extract and matches it with the correct program for opening that file. This script was meant to be used as a zsh plugin, but I repurposed it. In doing so I needed to change the output directory (initially files were being extracted to my user's home directory) and make it automatically overwrite existing directories since we wanted this process to be automatic.

The .desktop file allows the script to be registered as the default program for a given mime-type. All you have to do is open one archive with a given extension using the extract menu option and that's it, extract is now the default program for opening those archives.

Now for the little bit of setup:

Any archive formats you want to have automatically extract need to be opened with extract so that the default for that archive type can be changed. To do this (on GNOME) right click an archive, select "Open With Other Application", click "Find New Applications" at the bottom of the dialog, and then find extract in the list. If you don't see it you may need to log out and then log back in again.

That's really it. Now download an archive and try opening it directly from the downloads menu in your browser. The archive should be extracted and then the directory automatically opened.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay