#!/usr/bin/env bash # shellcheck disable=SC2034 ######################################################################################## if [[ -n "${SCRIPT_DEBUG}" ]] ; then set -x fi ######################################################################################## APP="7zcat" VER="2.1.0" ######################################################################################## NORM=0 BOLD=1 ITLC=3 UNLN=4 RED=31 GREEN=32 YELLOW=33 BLUE=34 MAG=35 CYAN=36 GREY=37 DARK=90 CL_NORM="\e[${NORM}m" CL_BOLD="\e[${BOLD}m" CL_ITLC="\e[${ITLC}m" CL_UNLN="\e[${UNLN}m" CL_RED="\e[${RED}m" CL_GREEN="\e[${GREEN}m" CL_YELLOW="\e[${YELLOW}m" CL_BLUE="\e[${BLUE}m" CL_MAG="\e[${MAG}m" CL_CYAN="\e[${CYAN}m" CL_GREY="\e[${GREY}m" CL_DARK="\e[${DARK}m" CL_BL_RED="\e[${BOLD};${RED}m" CL_BL_GREEN="\e[${BOLD};${GREEN}m" CL_BL_YELLOW="\e[${BOLD};${YELLOW}m" CL_BL_BLUE="\e[${BOLD};${BLUE}m" CL_BL_MAG="\e[${BOLD};${MAG}m" CL_BL_CYAN="\e[${BOLD};${CYAN}m" CL_BL_GREY="\e[${BOLD};${GREY}m" CL_IT_DARK="\e[${ITLC};${DARK}m" ######################################################################################## SUPPORTED_OPTS="password !fast !no_color !help !version" SHORT_OPTS="p:password F:!fast nc:!no_color h:!help v:!version" ######################################################################################## prompt="" masksym="" binary="7za" options="-so -bd" ######################################################################################## main() { if [[ ! -c /dev/stdout || -n "$no_color" || -n "$NO_COLOR" ]] ; then unset NORM BOLD CL_ITLC UNLN RED GREEN YELLOW BLUE MAG CYAN GREY DARK unset CL_NORM CL_BOLD CL_UNLN CL_RED CL_GREEN CL_YELLOW CL_BLUE CL_MAG CL_CYAN CL_GREY CL_DARK unset CL_BL_RED CL_BL_GREEN CL_BL_YELLOW CL_BL_BLUE CL_BL_MAG CL_BL_CYAN CL_BL_GREY CL_IT_DARK no_color=true fi if [[ -n "$version" ]] ; then about exit 0 fi if [[ -n "$help" || $# -eq 0 ]] ; then usage exit 0 fi prepare process "$@" exit $? } # Prepare for script execution # # Code: No # Echo: No prepare() { if [[ -z "$BINARY" ]] ; then if ! type -P 7za &>/dev/null ; then if ! type -P 7zz &>/dev/null ; then error "7zip/p7zip not installed" exit 1 else binary="7zz" fi fi else binary="$BINARY" fi if [[ -z "$fast" ]] ; then options="-mmt1 $options" fi if ! printenv | grep 'LANG' | grep -qi 'UTF-8' ; then prompt="> " masksym="*" else prompt="› " masksym="${CL_DARK}•${CL_NORM}" fi if [[ "$password" == "true" ]] ; then readUserPassword fi } # Process files # # *: All arguments passed to script # # Code: Yes # Echo: No process() { local file has_errors for file in "$@" ; do if ! checkFile "$file" ; then has_errors=true continue fi if isProtectedFile "$file" && [[ -z "$password" ]] ; then readUserPassword "$file" fi if [[ -n "$password" ]] ; then if ! readFile "$file" ; then error "Wrong password" has_errors=true fi else if ! readFile "$file" ; then has_errors=true fi fi done if [[ -n "$has_errors" ]] ; then return 1 fi return 0 } # Read password # # 1: Path to file (String) # # Code: No # Echo: No readUserPassword() { local file_name file_name=$(basename "$1") while : ; do if [[ -n "$file_name" ]] ; then show "Please enter password for $file_name:" $GREY else show "Please enter password:" $GREY fi read -re -p "${prompt}" user_pass maskPassword ${#user_pass} show "" if [[ -z "$user_pass" ]] ; then warn "Password can't be empty" else password="$user_pass" break fi done } # Check file before unpacking # # 1: Path to file (String) # # Code: Yes # Echo: No checkFile() { local file="$1" if [[ ! -e "$file" ]] ; then error "File ${CL_BL_RED}${file}${CL_RED} doesn't exist" return 1 fi if [[ ! -f "$file" ]] ; then error "${CL_BL_RED}${file}${CL_RED} is not a file" return 1 fi if [[ ! -r "$file" ]] ; then error "File ${CL_BL_RED}${file}${CL_RED} is not readable" return 1 fi if ! is7zArch "$file" ; then error "File ${CL_BL_RED}${file}${CL_RED} is not a 7z archive" return 1 fi return 0 } # Read file # # 1: Path to file (String) # # Code: Yes # Echo: No readFile() { local file="$1" local cmd_options="$options" if [[ -n "$password" ]] ; then cmd_options="$cmd_options -p${password}" fi # shellcheck disable=SC2086 "$binary" e $cmd_options "$file" 2>/dev/null return $? } # Check if given file is 7z archive # # 1: Path to file (String) # # Code: Yes # Echo: No is7zArch() { "$binary" l "$1" &> /dev/null return $? } # Check if archive use password protection # # 1: Path to file (String) # # Code: Yes # Echo: No isProtectedFile() { if [[ $("$binary" l "$1" | grep "7zAES") != "" ]] ; then return 0 fi return 1 } # Replace entered password by masking symbols # # 1: Password length (Number) # # Code: No # Echo: No maskPassword() { local i result result="${result}${prompt}" for i in $(seq 1 "$1") ; do result="${result}${masksym}" done result=$(echo -e "$result") printf "\e[1A%s\n" "$result" } ######################################################################################## # Print message # # 1: Message (String) # 2: Message color (Number) [Optional] # # Code: No # Echo: No show() { if [[ -n "$2" && -z "$no_color" ]] ; then echo -e "\e[${2}m${1}\e[0m" else echo -e "$*" fi } # Print formatted message with option info # # 1: Option (String) # 2: Description (String) # 3: Arguments (String) [Optional] # # Code: No # Echo: No showo() { local optn optd optp optt optf breadcrumbs optn="$1" optd="$2" optp="$3" if [[ -n "$DARK" ]] ; then breadcrumbs="............................" else breadcrumbs=" " fi if [[ -z "$optp" ]] ; then optt="$optn" optf=$(( 25 - ${#optt} )) echo -e " ${CL_GREEN}${optn}${CL_NORM} ${CL_DARK}${breadcrumbs:0:$optf}${CL_NORM} $optd" else optt="$optn $optp" optf=$(( 25 - ${#optt} )) echo -e " ${CL_GREEN}${optn}${CL_NORM} ${CL_GREY}${optp}${CL_NORM} ${CL_DARK}${breadcrumbs:0:$optf}${CL_NORM} $optd" fi } # Print error message # # 1: Message (String) # 2: Message color (Number) [Optional] # # Code: No # Echo: No error() { show "▲ $*" $RED 1>&2 } # Print warning message # # 1: Message (String) # # Code: No # Echo: No warn() { show "▲ $*" $YELLOW 1>&2 } # Print usage info # # Code: No # Echo: No usage() { show "" show "${CL_BOLD}Usage:${CL_NORM} 7zcat ${CL_GREEN}{options}${CL_NORM} file…" show "" show "Options" $BOLD show "" showo "--password, -p" "Use password for unpacking archives" "password" showo "--fast, -F" "Use all CPUs for unpacking data" showo "--no-color, -nc" "Disable colors in output" showo "--help, -h" "Show this help message" showo "--version, -v" "Show information about version" show "" show "Examples" $BOLD show "" show " 7zcat file.7z" show " ${CL_IT_DARK}Show content of file.7z${CL_NORM}" show "" show " 7zcat -p test1234 protected-file.7z" show " ${CL_IT_DARK}Show content of password protected file${CL_NORM}" show "" show " 7zcat file.7z | grep error" show " ${CL_IT_DARK}Filter content of file.7z${CL_NORM}" show "" } # Show info about version # # Code: No # Echo: No about() { local z7ver z7ver=$(7zz 2>/dev/null | head -2 | tail -1 | cut -f3 -d' ') pz7ver=$(7za 2>/dev/null | head -2 | tail -1 | cut -f4 -d' ') show "" show "${CL_BL_CYAN}$APP${CL_NORM} ${CL_CYAN}$VER${CL_NORM} - Utility for output data in 7z archive to stdout" show "│" $DARK if [[ -n "${z7ver}" ]] ; then show "└ 7zip: ${z7ver}" $DARK else show "└ p7zip: ${pz7ver:-—}" $DARK fi show "" show "Copyright (C) 2009-$(date +%Y) ESSENTIAL KAOS" $DARK show "Apache License, Version 2.0 " $DARK show "" } # Show warning message about unsupported option # # 1: Option name (String) # # Code: No # Echo: No showOptWarn() { error "Unknown option $1" exit 1 } ## OPTIONS PARSING 5 ########################################################### if [[ $# -eq 0 ]] ; then main fi unset opt optn optm optv optt optk optv="$*" ; optt="" while [[ -n "$1" ]] ; do if [[ "$1" =~ \ && -n "$optn" ]] ; then declare "$optn=$1" unset optn && shift && continue elif [[ $1 =~ ^-{1}[a-zA-Z0-9]{1,2}+.*$ ]] ; then optm=${1:1} if [[ \ $SHORT_OPTS\ =~ \ $optm:!?([a-zA-Z0-9_]*) ]] ; then opt="${BASH_REMATCH[1]}" else declare -F showOptWarn &>/dev/null && showOptWarn "-$optm" shift && continue fi if [[ -z "$optn" ]] ; then optn=$opt else # shellcheck disable=SC2015 [[ -z "$optk" ]] && ( declare -F showOptValWarn &>/dev/null && showOptValWarn "--$optn" ) || declare "$optn=true" optn=$opt fi if [[ ! $SUPPORTED_OPTS\ =~ !?$optn\ ]] ; then declare -F showOptWarn &>/dev/null && showOptWarn "-$optm" shift && continue fi if [[ ${BASH_REMATCH[0]:0:1} == "!" ]] ; then declare "$optn=true" ; unset optn ; optk=true else unset optk fi shift && continue elif [[ "$1" =~ ^-{2}[a-zA-Z]{1}[a-zA-Z0-9_-]+.*$ ]] ; then opt=${1:2} if [[ $opt == *=* ]] ; then IFS="=" read -ra opt <<< "$opt" optm="${opt[0]}" ; optm=${optm//-/_} if [[ ! $SUPPORTED_OPTS\ =~ $optm\ ]] ; then declare -F showOptWarn &>/dev/null && showOptWarn "--${opt[0]//_/-}" shift && continue fi # shellcheck disable=SC2015 [[ -n "${!optm}" && $MERGEABLE_OPTS\ =~ $optm\ ]] && declare "$optm=${!optm} ${opt[*]:1:99}" || declare "$optm=${opt[*]:1:99}" unset optm && shift && continue else # shellcheck disable=SC2178 opt=${opt//-/_} if [[ -z "$optn" ]] ; then # shellcheck disable=SC2128 optn=$opt else # shellcheck disable=SC2015 [[ -z "$optk" ]] && ( declare -F showOptValWarn &>/dev/null && showOptValWarn "--$optn" ) || declare "$optn=true" # shellcheck disable=SC2128 optn=$opt fi if [[ ! $SUPPORTED_OPTS\ =~ !?$optn\ ]] ; then declare -F showOptWarn &>/dev/null && showOptWarn "--${optn//_/-}" shift && continue fi if [[ ${BASH_REMATCH[0]:0:1} == "!" ]] ; then declare "$optn=true" ; unset optn ; optk=true else unset optk fi shift && continue fi else if [[ -n "$optn" ]] ; then # shellcheck disable=SC2015 [[ -n "${!optn}" && $MERGEABLE_OPTS\ =~ $optn\ ]] && declare "$optn=${!optn} $1" || declare "$optn=$1" unset optn && shift && continue fi fi optt="$optt $1" ; shift done [[ -n "$optn" ]] && declare "$optn=true" unset opt optn optm optk # shellcheck disable=SC2015,SC2086 [[ -n "$KEEP_OPTS" ]] && main $optv || main ${optt:1} ################################################################################