Скрипт для распаковки архивов разного типа

Просто ищу отзывы и конструктивную критику. Это второй сценарий, который я когда-либо делал. Совсем недавно начал изучать азы. В любом случае просто хотел опубликовать сценарий и посмотреть, не смогу ли я получить какие-то данные о том, где я могу что-то сделать лучше.

Скрипт предназначен для извлечения сжатых типов файлов, с которыми вы чаще всего сталкиваетесь в Linux. Я добавил возможность извлечения в каталог по выбору пользователя, а не просто ограничивать его только рабочим каталогом.

Я чувствую, что у меня слишком много if/then заявления, и они могут быть как-то лучше, но я не уверен. И не уверен, правильно ли я реализовал выходы. Некоторые из вас, вероятно, вздрогнут, когда увидят это лол! В любом случае, если вы можете помочь, спасибо.

#!/bin/bash

error() {
    echo "For help, type: nzip -h"
}

usage() {
    echo "Usage: nzip [FILE]..."
    echo "By default file(s) will be extracted to the working directory."
    echo
    echo "OPTIONS.."
    echo
    echo "-d, --directory, eg. nzip [file] [-d] [directory]"
    echo
    echo
}

if [ $# -eq 0 ]
then
    error >&2
    exit 1
fi

if [ $# -eq 1 ]
then
    case $1 in
        -h) usage
            exit 0
            ;;
        *.7z) 7z e "$1"
            ;;
        *.tar.bz2) tar xjvf "$1"
            ;;
        *.tar.gz) tar xzvf "$1"
            ;;
        *.rar) unrar e "$1"
            ;;
        *.tar) tar xvf "$1"
            ;;
        *.bz2) bunzip2 "$1"
            ;;
        *.tbz) tar xjvf "$1"
            ;;
        *.gz) gunzip "$1"
            ;;
        *.tgz) tar xzvf "$1"
            ;;
        *.jar) unzip "$1"
            ;;
        *.xz) tar xvf "$1"
            ;;
        *.zip) unzip "$1"
            ;;
        *.Z) uncompress "$1"
            ;;
        *) echo "No extraction option for $1" >&2
            exit 1
    esac
    exit 0
fi

if [ $# -eq 3 ] && [ "$2" = "-d" ]
then
    case $1 in
        *.7z) 7z e "$1" -o"$3"
            ;;
        *.tar.bz2) tar xjvf "$1" -C "$3"
            ;;
        *.tar.gz) tar xzvf "$1" -C "$3"
            ;;
        *.rar) unrar e "$1" "$3"
            ;;
        *.tar) tar xvf "$1" -C "$3"
            ;;
        *.bz2) bunzip2 "$1" -c > "$3"
            ;;
        *.tbz) tar xjvf "$1" -C "$3"
            ;;
        *.gz) gunzip "$1" -c > "$3"
            ;;
        *.tgz) tar xzvf "$1" -C "$3"
            ;;
        *.jar) unzip "$1" -d "$3"
            ;;
        *.xz) tar xvf "$1" -C "$3"
            ;;
        *.zip) unzip "$1" -d "$3"
            ;;
        *.Z) uncompress "$1" -c > "$3"
            ;;
        *) echo "No extraction option for $1" >&2
            exit 1
    esac
else
    error >&2
    exit 1
fi
```

2 ответа
2

Я не уверен, почему у нас #!/bin/bash — выглядит обычным, портативным #!/bin/sh здесь было бы хорошо.

Что мне сразу нравится, это хорошая обработка ошибок, использование &2 для сообщений об ошибках и выхода ненулевое значение.

Все tar команды можно комбинировать, как tar способен (с -a), чтобы автоматически идентифицировать любую схему сжатия, которую он обрабатывает.

Поскольку после запуска архиватора нет необходимости продолжать, мы можем заменить процесс оболочки, используя exec. Например

case $1 in
    *.7z) exec 7z e "$1"
        ;;

Не делайте этого, если мы расширим программу для обработки нескольких входных файлов (см. Ниже).

Если мы сначала возьмем обычное соглашение о флагах, тогда будет проще, когда мы решим, что хотим обрабатывать больше параметров (например, -v, поэтому мы можем по умолчанию работать в тихом режиме). И мы можем объединить два больших case создает, всегда передавая каталог, но по умолчанию в текущий рабочий каталог:

destination=.

die() {
    printf '%sn' "$@" >&2
    exit 1
}

do_extract() {
    case $1 in
        *.tar.*|*.t[bg]z) tar xaf "$1" -C "$destination"
            ;;
        *.jar|*.zip) unzip "$1" -d "$destination"
            ;;
        # ...
    esac
}

while [ $# -ge 1 ]
do
    case "$1" in
        -d)
            [ $# -gt 1 ] || die "Usage: $0 [-d directory] file..."
            destination=$2
            shift 2
            ;;
        -*)
            die "Unrecognised option: $1"
        *)
            do_extract "$1"
            shift
    esac
done

Будь осторожен с echo:

usage() {
    echo "Usage: nzip [FILE]..."
    echo "By default file(s) will be extracted to the working directory."
    echo
    echo "OPTIONS.."
    echo
    echo "-d, --directory, eg. nzip [file] [-d] [directory]"
    echo
    echo
}

Другие (например, будущие) версии echo может относиться к этому -d как обозначение флага.

Я был бы склонен использовать для этого файл:

usage() {
    cat <<'END'
Usage: nzip [FILE] [-d DESTINATION]
By default file(s) will be extracted to the working directory.

OPTIONS:
    -d destination
END
}

Обратите внимание, что я изменил строку использования, потому что мы поддерживаем только один файл, и -d и аргумент назначения идут вместе (и неправильно называть его «каталогом», когда это может быть не имя каталога, если мы распаковываем один файл).

  • спасибо за всю эту прекрасную информацию. Я реализовал большую часть того, что вы предложили. по крайней мере, те части, которые я понял. Мне еще многому нужно научиться. я знаю, что «-eq» равно, означает ли «-ge» больше или равно? а «-gt» просто больше, чем? также не уверен, что делает сдвиг. и почему бы просто не установить destination = $ 2 с самого начала.

    — ядерная бомба


  • 1

    Да, вы правильно угадали этих операторов. man test или же мужчина [ for the full details (though Bash has its own test built-in, so man bash-builtins is probably what you want; that’s also where to find a description of shift).

    – Toby Speight

  • 1

    The while [ $# -ge 1 ] (или просто while [ "$@" ]) цикл с shift — довольно распространенная идиома для чтения аргументов опций в сценариях оболочки; ожидайте увидеть это довольно часто при работе с оболочкой.

    — Тоби Спейт

Основываясь на отличном ответе Тоби, это предлагает 2 альтернативных способа анализа вариантов.

#!/usr/bin/env bash

readonly PROGRAM=${0##*/}

readonly USAGE=$(cat <<END_USAGE
Usage: $PROGRAM [FILE]...

By default file(s) will be extracted to the working directory.

OPTIONS

-d, --directory, eg. nzip [file] [-d directory]

END_USAGE
)

die()   { printf '%sn' "$*" >&2; exit 1; }
error() { die "For help, type: $PROGRAM -h"; }
usage() { echo "$USAGE"; exit 0; }

extract() {
    local file=$1 dest=$2
    echo "various ways to decompress $file to $dest"
}

# default destination is the current directory
destination="."


############################################################
# parse options

Теперь мы можем использовать встроенный bash getopts для анализа параметров, но это позволяет только короткие варианты

while getopts :hd: opt; do
    case $opt in
        h) usage ;;
        d) destination=$OPTARG ;;
        :) die "Missing option for -$OPTARG" ;;
        *) error ;;
    esac
done
shift $((OPTIND - 1))

Или используйте внешний getopt инструмент, чтобы разрешить длинные варианты тоже

tmp=$( getopt -o 'hd:' --long 'help,directory:' -n "$PROGRAM" -- "$@" ) || exit $?
eval set -- "$tmp"

while true; do
    case $1 in
        '-h'|'--help') usage ;;
        '-d'|'--directory') 
            destination=$2
            shift 2
            ;;
        '--')
            shift
            break
            ;;
        *)  die 'Internal error' ;;
    esac
done

А потом:

############################################################
# after option parsing

(( $# > 0 )) || usage

for file in "$@"; do
    extract "$file" "$destination"
done

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *