How to extract files from archives efficiently

From Vifm Wiki
Revision as of 21:13, 9 September 2017 by Xaizek (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Performance of FUSE mounts

Since FUSE file systems can be used with vifm to browse archives and one of the most common task with an archive is extraction of files it contains, it's natural to use copy operation to do that in vifm. Unfortunately there are some issues with performance, which make this significantly slower than it should be. This doesn't seem to be an artifact of FUSE architecture, but rather unoptimized implementation of FUSE mounters. Copying is an I/O intensive operation and vifm sends many requests and because FUSE mounters don't seem to cache even path resolution each request ends up taking a lot of time (relative to time necessary to transfer data itself).

As a workaround this page presents a script, which can be invoked from vifm to extract files from an archive by using 7z and without use of FUSE.

Warning

The script wasn't thoroughly tested, so check that it works correctly before relying on it.

Script

#!/usr/bin/bash
 
if [ -z "$VIFM_FUSE_FILE" ]; then
    echo "error: this command is meant to be run inside vifm FUSE mount"
    exit 1
fi
 
archive="$VIFM_FUSE_FILE"
filename="${archive##*/}"
 
#Because 7z can't extract these files in one call:
doubleExtraction=0
if [[ $filename =~ ^.*(tar\.gz|tar\.bz2)$ ]] ;
then
    doubleExtraction=1
fi
 
fusehome="$1"
shift
destination="$1"
shift
 
tempDest=`mktemp -d`
 
files=
for f; do
    # obtain relative path inside archive
    f="${f#$fusehome}"
    f="${f#/}"
    f="${f#*/}"
 
    files=( ${files[@]} "$f" )
done
 
firstFile=${files[@]:0:1}
IFS='/' read -ra ADDR <<< $firstFile
unset ADDR[${#ADDR[@]}-1]
 
movePath=$tempDest/
for i in "${ADDR[@]}"; do
    movePath=$movePath$i/
done
movePath=$movePath*
 
if [[ $doubleExtraction = 1 ]]; then
    exec 7z x -so "$archive" | 7z x -si -ttar -o$tempDest "${files[@]}" > /dev/null 2>&1 && mv -i $movePath $destination && rm -r $tempDest
else
    exec 7z x -o$tempDest "$archive" "${files[@]}" > /dev/null 2>&1 && mv -i $movePath $destination && rm -r $tempDest
fi

vifmrc part

Assuming that the script is called extract-files and is somewhere in $PATH, the command to add to vifmrc is:

command! extractfiles :execute '!%%n extract-files' &fusehome expand('%%D') expand('%%f:p')

How to use

When inside FUSE mounted archive, select files you want to extract into current directory of the inactive pane and run :extractfiles command.

Sources

Issue #71 on GitHub