Запись загрузочного образа на USB-устройство


Недавно, я делал и нефтеперерабатывающие установки и спасение изображений для загрузки с USB-носителя в виде компакт-диска или жесткого диска изображения. После нескольких случаев, когда я заново запустил команду после удаления СМИ (и заканчивая большой файл в /dev/disk/by-id/ вместо записи на устройство), я написал небольшую обертку, чтобы убедиться, что цель-это блочное устройство.

С тех пор он вырос функций, в частности для получения данных непосредственно из сети (например, сервер сборки), чтобы сэкономить, сделав локальную копию первого. В этих случаях, я хочу быть в состоянии sudo write-image но все равно сделать подключение к сети, как не-root пользователей (частично как хорошая практика, но также, Так что я могу использовать мой ~/.ssh/config и закрытый ключи в обычном режиме).

В usage() функции должны объяснить, как это предназначается, чтобы использоваться; если я должен объяснить далее, то это признак того, что моя документация нуждается в улучшении.

Почему это должен Баш, а не оболочка POSIX заключается в использовании команды замена процесс подмены. Использование функции Shell по импортозамещению процесс, похоже, особенно не портативный, но это работает для меня с Башем 4.4.

#!/bin/bash

set -eu

die() { echo "$@" >&2; exit 1; }

usage() {
    cat <<END
usage: $0 [source] <usb-device>
The source can be
  * a filename
  * a URL
  * ssh:[user@]<host>:<file>  (must be passwordless)
  * '-' or absent (for piping from standard input)
END
}

as_real_user() {
    exec ${SUDO_USER+sudo -n -H -u "$SUDO_USER" }"$@"
}

case $# in
    1)
        case "$1" in
            --help|-h)
                usage
                exit 0
                ;;
        esac
        ;;
    2)
        case "$1" in
            ssh:*)
                file="${1#*:}"
                host="${file%%:*}"
                file="${file#*:}"
                exec < <(as_real_user ssh "$host" cat "$file")
                ;;
            *://*)
                exec < <(as_real_user wget -q -O - "$url")
                ;;
            -)
                # standard input is already connected
                ;;
            *)
                test -e "$1" || die "$1: not found"
                test -f "$1" || die "$1: not a plain file"
                test -r "$1" || die "$1: not readable"
                exec < "$1"
                ;;
        esac
        shift
        ;;
    *)
        usage
        exit 1;
esac

test -b "$1" || die "$1: not a block device"
test -w "$1" || die "$1: not writable"
cut -d' ' -f1 /proc/mounts | grep -Fxq $(realpath "$1") && die "$1: is mounted"
outfile="of=$1"

# Explanation
# * nocreat - just checking, given above tests of destination
# * fsync - don't return until image is fully written
# * dsync,bs,status - show update every 1 MB written
ddflags="conv=nocreat,fsync oflag=dsync bs=1M status=progress"

exec dd $ddflags $outfile


138
4
задан 14 марта 2018 в 04:03 Источник Поделиться
Комментарии
3 ответа

Цитировать

Некоторые рекомендуемые двойные кавычки были пропущены здесь:


cut -d' ' -f1 /proc/mounts | grep -Fxq $(realpath "$1") && die "$1: is mounted"

Должен быть такой:

cut -d' ' -f1 /proc/mounts | grep -Fxq "$(realpath "$1")" && die "$1: is mounted"


Это нарушит к примеру, если $outfile содержит пробелы:


exec dd $ddflags $outfile

Вы могли бы сделать его безопасным с помощью массива:

ddargs=(conv=nocreat,fsync oflag=dsync bs=1M status=progress "of=$1")

exec dd "${ddargs[@]}"

Совет Безопасности

set -eu хороший защиты, я также хотел бы добавить -o pipefail:

set -euo pipefail

Косметические проблемы

Есть ненужный трейлинг ; в exit 1;

2
ответ дан 19 марта 2018 в 09:03 Источник Поделиться

Недосмотр

Я забываю, чтобы перенаправить сообщение в стандартный поток ошибок здесь:

*)
usage >&2
exit 1;

Опечатка

Я, очевидно, не проверить сеть Ури филиал после того, как я убрал $url переменной:

        *://*)
exec < <(as_real_user wget -q -O - "$url")
;;

Что должно быть $1не $url.

Лучше проверять СШ путей

Мы никогда не проверяем, что $file можно разделить на хост и путь частей. Это легко сделать:

        ssh:*:*)
file="${1#*:}"
host="${file%%:*}"
file="${file#*:}"
exec < <(as_real_user ssh "$host" cat "$file")
;;
ssh:*)
die "Malformed remote name - should be ssh:[user@]<host>:<file>"
;;

1
ответ дан 14 марта 2018 в 04:03 Источник Поделиться

Я не знаю как


использование функции Shell в командной замена похоже, особенно непереносимому

но насколько я знаю, команду можно записать как `...` или $(...)и последнее менее портативный, как указано в стандарте POSIX. Процесс замещения <(...)С другой стороны, возник в Корн Шелл и действительно непереносимую.

Я бы, наверное, удалить set -eu из сценария или ограничить его влияние на небольшой блок кода после определенного количества испытаний. Причина такова, вкратце, что эта функция не является ни надежным, ни портативного (прочитайте эту статью для более подробной информации), и так как вы уже делаете некоторые ошибки проверять себя, вы не должны иметь его.

Просто комментарий, но test это очень архаичный способ выполнения многих файл и строку проверки. Это был единственный способ сделать эту функцию перед [ был введен в качестве оболочки строение-давно, еще в далеком прошлом. Вы, вероятно, увидеть это использование более часто в autoconf для код только из-за ограничения М4 синтаксис:


Тестовая программа-это способ выполнять многие файловые и строковые тесты. Это часто вызывается альтернативное имя ‘[’, но использует это имя в код автонастройки может привести к неприятностям, поскольку это М4 кавычки.

Я не уверен, какой из следующих является более портативным, если они даже достичь то же самое: cut -d' ' -f1 /proc/mounts или df -Pa | cut -d' ' -f1 | tail -n +2. Я просто отправляю это здесь в качестве пищи для размышления. О, и realpath команда не портативный.

1
ответ дан 12 апреля 2018 в 02:04 Источник Поделиться