Skip to content

Instantly share code, notes, and snippets.

@jsamr
Last active April 4, 2021 23:23
Show Gist options
  • Save jsamr/b55ed800eea22019bea5881fca000851 to your computer and use it in GitHub Desktop.
Save jsamr/b55ed800eea22019bea5881fca000851 to your computer and use it in GitHub Desktop.
A bash script to very easily create a bootable USB device from one ISO file. Just curl it, chmod it and go!
#!/bin/bash
# Author: jules randolph <jules.sam.randolph@gmail.com> https://github.com/jsamr
# License: MIT
#
# Usage: [<options>] <file.iso>
#
# Create a bootable FAT32 USB device from a linux-GNU/unix ISO.
#
# Options
# -n, --no-bootloader Do not add syslinux bootloader.
# -d, --device <device> Select <device> as USB device.
# Device should be in the form /dev/sXX or /dev/hXX.
# You will be prompted to select a device if you don't use this option.
# -f, --force-iso Disable mime-type check on ISO file.
#
# How it works
#
# The script walks through the following steps:
# 0. Check dependencies and prompt user to install any missing.
# 1. Check provided ISO exists and has the expected application/x-iso9660-image mime-type.
# 2. If given with -d option, check that the selected device exists and is not a partition. Otherwise, prompt the user to select a device.
# 3. Prompt the user for confirmation that data might be lost for selected device if he goes to next step.
# 4. Unmount the USB if mounted, blank it and delete existing partitions.
# 5. Create a FAT32 partition on the USB device.
# 6. Create a temporary dir to mount the ISO file and mount it.
# 7. Create a temporary dir to mount the USB device and mount it.
# 8. Copy files from ISO to USB device.
# 9. If option --no-bootloader is not selected, install a bootloader with syslinux in slow mode.
# 10. Unmount devices and remove temporary folders.
dependencies=('lsblk' 'grep' 'file' 'sfdisk' 'mkfs.vfat' 'syslinux' 'mtools' 'rsync')
scriptName=$(basename "$0")
selectedDrive=''
selectedPartition=''
selectedIsoFile=''
isoMountPoint=''
usbMountPoint=''
# options
addSyslinuxBootloader=true
disableMimeCheck=false
help_message="\
Usage: $scriptName [<options>] <file.iso>
Create a bootable FAT32 USB from a linux-GNU/unix ISO.
Options
-n, --no-bootloader Do not add syslinux bootloader.
-d, --device <device> Select <device> as USB device.
Device should be in the form /dev/sXX or /dev/hXX.
You will be prompted to select a device if you don't use this option.
-f, --force-iso Disable mime-type check on iso file.
"
display_help() {
echo "$help_message"
}
echoerr() {
>&2 echo -e "\\033[0;31m$1\\033[0m"
}
echowarn() {
echo -e "\\033[1;33m$1\\033[0m"
}
failAndExit() {
echoerr "$1\\nExiting $scriptName..."
exit 1
}
initPckgManager() {
if [ ! -z "$(which apt-get)" ]; then # Debian
pkgmgr="apt-get -y install"
return 0
fi
if [ ! -z "$(which yum)" ]; then # Fedora
pkgmgr="yum -y install"
return 0
fi
if [ ! -z "$(which dnf)" ]; then # Fedora
pkgmgr="dnf -y install"
return 0
fi
if [ ! -z "$(which pacman)" ]; then # Arch
pkgmgr="pacman -S"
return 0
fi
if [ ! -z "$(which zypper)" ]; then # OpenSuse
pkgmgr="zypper install"
return 0
fi
if [ ! -z "$(which emerge)" ]; then # Gentoo
pkgmgr="emerge"
return 0
fi
}
init() {
if ((EUID != 0)); then
if [[ -t 1 ]]; then
sudo "$0" "$@"
else
exec 1>output_file
gksu "$0" "$@"
fi
exit
fi
initPckgManager
}
parseOptions() {
typeset key
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-n|--no-bootloader)
addSyslinuxBootloader=false
shift
;;
-d|--device)
selectedDrive="$2"
shift
shift
;;
-f|--force-iso)
disableMimeCheck=true
shift
;;
-*)
if [ ! -f "$key" ]; then
failAndExit "Unknown option \`$key'."
fi
selectedIsoFile=$1
;;
*)
selectedIsoFile=$1
shift
;;
esac
done
if [ "$selectedIsoFile" == '' ]; then
failAndExit "Missing argument \`iso-file'."
fi
if [ ! -e "$selectedIsoFile" ]; then
failAndExit "Provided iso file \`$selectedIsoFile' does not exists."
fi
typeset mimetype=$(file --mime-type -b "$selectedIsoFile")
if [ ! "$mimetype" == "application/x-iso9660-image" ] && [ "$disableMimeCheck" == 'false' ]; then
failAndExit "Provided file \`$selectedIsoFile' doesn't seem to be an iso file (wrong mime type: $mimetype)."
fi
}
checkPackages() (
checkpkg() {
if [[ $(which "$1") == "" ]]; then
echo -n "Package '$1' not found!"
if [[ ! $pkgmgr == "" ]]; then
read -r -n1 -p "Attempt installation? (y/n)>" answer
echo
case $answer in
y) $pkgmgr "$1"
;;
n)
read -r -n1 -p "Proceed anyway? (y/n)>" answer2
echo
if [[ "$answer2" == "n" ]] ; then exit
fi
;;
esac
else
failAndExit "Missing dependency \`$1'."
fi
fi
}
for pkg in "${dependencies[@]}"; do
checkpkg "$pkg"
done
)
# print the name of the new folder if operation succeeded, fails otherwise
# arg1 : template name
createTempFolder() {
typeset tmpFileTemplate="/tmp/$1.XXX"
mktemp -d "$tmpFileTemplate"
typeset status=$?
if [ ! $status -eq 0 ]; then
failAndExit "Failed to create temporary folder"
fi
}
mountIsoFile() {
isoMountPoint=$(createTempFolder iso) || exit 1
echo "Created iso mount point at \`$isoMountPoint'"
if ! mount -o loop "$selectedIsoFile" "$isoMountPoint"; then
failAndExit "Could not mount iso file."
fi
}
selectDrive() {
typeset drives=()
typeset _selectedDrive
containsElement () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
listDrives() {
lsblk | grep --color=never -oe "^[sh][a-zA-Z]*"
}
chooseDrive() {
echo "Select the device corresponding to the USB device you want to make bootable (${drives[*]}). Type exit to quit."
read -r -p "Select device name>" _selectedDrive
if containsElement "$_selectedDrive" "${drives[@]}"; then
selectedDrive="/dev/$_selectedDrive"
echo "Selected device is \`$selectedDrive'"
else
if [ "$_selectedDrive" == 'exit' ]; then
echo "Exiting on user request."
exit 0
else
failAndExit "The drive $_selectedDrive does not exist."
fi
fi
}
if [ "$selectedDrive" == '' ]; then
mapfile -t drives < <(listDrives)
# List all hard disk drives
echo "Listing available drives in your system:"
lsblk | grep --color=never -e '^[sh]'
chooseDrive
else
if [[ "$selectedDrive" =~ "/dev" ]]; then
if [ -e "$selectedDrive" ]; then
if [[ "$selectedDrive" =~ /dev/[a-zA-Z]+[0-9]+ ]]; then
failAndExit "\`$selectedDrive' is a partition. Select a drive instead."
else
echo "Selected device is: $selectedDrive"
fi
else
failAndExit "The device $selectedDrive does not exists"
fi
else
failAndExit "The selected device is not a valid posix device file (should start with \`/dev')"
fi
fi
}
hasDeviceMountedPartitions() {
typeset result=$(mount | grep -e "^$selectedDrive")
if [ "$result" == "" ]; then
return 1
else
return 0
fi
}
partitionUSB() {
typeset answer
echowarn "$scriptName is about to wipe out the content of device $selectedDrive."
selectedPartition="${selectedDrive}1"
read -r -n1 -p "Are you sure you want to proceed? (y/n)>" answer
if [ "$answer" == 'y' ]; then
echo "Erasing contents of $selectedDrive:"
# unmount
if hasDeviceMountedPartitions; then
if ! umount "$selectedDrive"?*; then
failAndExit "$scriptName failed to unmount paritions from drive $selectedDrive"
fi
fi
# erase drive
dd if=/dev/zero of="$selectedDrive" bs=512 count=1 conv=notrunc || failAndExit "Failed to erase USB device."
# set partition
echo "$selectedPartition : start=2048, type=b, bootable" | sfdisk "$selectedDrive" || failAndExit "Failed to write USB device partition table."
# format
echo "Creating fat32 partition..."
mkfs.vfat "$selectedPartition" > /dev/null || failAndExit "Failed to create fat32 partition on USB device."
else
failAndExit "Discarding operation."
fi
}
mountUSB() {
usbMountPoint=$(createTempFolder usb) || exit 1
echo "Created USB device mount point at \`$usbMountPoint'"
if ! mount -t vfat "$selectedPartition" "$usbMountPoint"; then
failAndExit "Could not mount USB device."
fi
}
createBootableUSB() {
echo "Copying files from iso to USB..."
rsync --info=progress2 --recursive --executability "$isoMountPoint"/. "$usbMountPoint"
if [ "$addSyslinuxBootloader" == 'true' ]; then
echo "Installing syslinux bootloader..."
syslinux --stupid "$selectedPartition"
if [ -d "$usbMountPoint/isolinux" ]; then
mv "$usbMountPoint/isolinux" "$usbMountPoint"/syslinux
fi
if [ -e "$usbMountPoint/syslinux/isolinux.cfg" ]; then
mv "$usbMountPoint/syslinux/isolinux.cfg" "$usbMountPoint"/syslinux/syslinux.cfg
fi
fi
}
main() {
init "$@"
parseOptions "$@"
checkPackages
selectDrive
mountIsoFile
partitionUSB
mountUSB
createBootableUSB
}
cleanup() {
if [ -d "$isoMountPoint" ]; then
echo "Unmounting iso..."
if umount "$isoMountPoint"; then
rmdir "$isoMountPoint"
fi
fi
if [ -d "$usbMountPoint" ]; then
echo "Unmounting USB..."
if umount "$usbMountPoint"; then
rmdir "$usbMountPoint"
fi
fi
}
trap cleanup EXIT INT TERM
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment