You are not logged in.
This is probably highly redundant... the chances are, someone will likely say "XYZ does that for you and you can configure it in 5 minutes", but here goes anyways.
I wanted a simple way to mount the disks in my computer to the same location regardless of where they were in the system (thus via UUID) but what I *didn't* want was to have to copy/type the UUID myself. The following possibly shaky bash script is the result.
First, however, a (very real-world) demonstration of its functionality!
/disks/ + ./domount
Using scriptdir "/disks/.mountscripts".
Running mount... [ok]
[Disk ST3250620A_5QE4M336]
group0-root -> /disks/250gb: [ok]
38067a33-0556-4cab-a5c5-c96b313bd174 -> /disks/250gb/boot: [ok]
21D4-2E62 -> /disks/250gb/data: [ok]
group0-home -> /disks/250gb/home:
== mount error ==
mount: wrong fs type, bad option, bad superblock on /dev/mapper/group0-home,
missing codepage or helper program, or other error
In some cases useful info is found in syslog - try
dmesg | tail or so
=================
[R]etry/Skip [P]artition/Skip [D]isk/[Q]uit? q
/disks/ + fsck.jfs /dev/mapper/group0-home
fsck.jfs version 1.1.15, 04-Mar-2011
processing started: 11/10/2011 20:28:10
Using default parameter: -p
The current device is: /dev/mapper/group0-home
Block size in bytes: 4096
Filesystem size in blocks: 52099072
**Phase 0 - Replay Journal Log
Filesystem is clean.
/disks/ + ./domount
Using scriptdir "/disks/.mountscripts".
Running mount... [ok]
[Disk ST3250620A_5QE4M336]
group0-root -> /disks/250gb: (already mounted)
38067a33-0556-4cab-a5c5-c96b313bd174 -> /disks/250gb/boot: (already mounted)
21D4-2E62 -> /disks/250gb/data: (already mounted)
group0-home -> /disks/250gb/home: [ok]
group0-var -> /disks/250gb/var: [ok]
partition1.vfat -> /disks/250gb/home/backup/80gb/mnt/partition1.vfat: [ok]
partition2.vfat -> /disks/250gb/home/backup/80gb/mnt/partition2.vfat: [ok]
partition3.vfat -> /disks/250gb/home/backup/80gb/mnt/partition3.vfat: [ok]
partition4.ext3 -> /disks/250gb/home/backup/80gb/mnt/partition4.ext3: [ok]
data2 -> /disks/250gb/home/backup/32gb-2/mnt/data2: [ok]
[Disk ST340014A_5MQ4HB90]
0854-08DE -> /disks/20gb-1/data-1: [ok]
4846-D7E2 -> /disks/20gb-1/data-2: [ok]
3070DB1E70DAE99C -> /disks/20gb-1/winnt: [ok]
38BB-158D -> /disks/20gb-1/pool: [ok]
[Disk WDC_WD800BB-22J_WD-WCAM9H677098]
e336c404-fca8-4f2b-9c75-81c22f339741 -> /disks/80gb: [ok]
4738-E723 -> /disks/80gb/vfat: [ok]
a827cfa1-08cf-4a24-a989-aae94ea0801b -> /disks/80gb/boot: [ok]
7bb5df89-3a90-4c92-8aa7-a94271806087 -> /disks/80gb/var: [ok]
09b652b7-4f5e-4895-8464-6f972a44fdd6 -> /disks/80gb/home: [ok]
a2534aa6-b70f-442d-805e-365ee626d4be -> /disks/80gb/tmpspace: [ok]
4871-993D -> /disks/80gb/tmpspace2: [ok]
386a2a83-22e2-425c-bd48-cb0a1fad8a87 -> /disks/80gb/pool: [ok]
/disks/ +
Here's the script! (I can pastebin it if neccessary)
#!/bin/bash
# ohai from i336 :P <i3367980@gmail.com>
# Oct-Nov 2011
# Public domain, no warranty. Be sure to use the "t" flag on the first run!
# This program has two modes: scan mode and run mode.
# Configuration
# =============
# You first need to create/go into the directory you want to mount your disks
# in, such as /mnt (I use /disks), and create the subdirectory ".mountscripts", or
# alternatively "programname-mountscripts" (the second directory bearing the
# name of the program/symlink, a simple mechanism to implement some flexibility).
# You can substitute any created symlinks whereever "./domount" is mentioned.
# The existance of this directory indicate that this is the work directory.
# (For added flexibility, the program will look for the second directory, the
# one bearing its name, first, then fall back on ".mountscripts" if this is not
# found.)
# Scan Mode
# =========
# After creating this directory for the first time you will want to run
# "./domount s" to generate the mountscripts into the mountscript directory
# (which is selected as specified above).
# Run Mode
# ========
# At this point, go into the mountscript directory, open all the files you find
# there in a text editor, and add in the mountpoints you want to use after the
# UUID parameter to 'partop' (an internal function defined in this file for the
# scripts).
#
# ** The first time you simply MUST run "./domount t" in order to see that the
# 'mount' commands are correct! **
#
# After this is done, run "./domount" and it will go ahead and mount the disks.
# Run "./domount u" and it will unmount everything. (No options exist for
# individual partitions as yet).
# Limitations
# ===========
# * If you use domount to mount loopback images inside real partitions and the
# real partitions are also mounted by domount, well, domount will try to
# unmount them in the same order as when it mounts... and it will break.
# Simple solution: skip however many real [p]artitions you have, then
# re-run domount again. :)
#
# * If you change a disk (eg add a partition), well, you'll have to delete the
# file for that disk, re-scan (domount will not touch the other scripts) then
# re-add your partitions back in. This program wasn't really designed to deal
# with that kind of situation :)
#
# * This program does not support LVM partitions - quite frankly, it doesn't
# even realize such things exist. Thus you will not find any LVM partitions
# listed in the generated scripts, or any "LVM partitions ignored"
# messages - indeed, if you only have LVM partitions on a given disk, the
# resulting syntactically incorrect script will contain an 'if' block with
# no content and the shell will produce an error.
toollist=
needtool=0
for tool in find lsblk blkid cfdisk xargs grep tail mountpoint; do
type -P $tool > /dev/null 2>&1
if [ $? -eq 0 ]; then
toollist="${toollist} ${tool}"
else
toollist="${toollist} [${tool}]"
needtool=1
fi
done
if [ $needtool -eq 1 ]; then
echo "This program requires the following tools in order to run. Those marked with"
echo "brackets cannot be found (using \`type') and their containing packages"
echo "likely need to be installed."
echo $toollist
exit 1
fi
sizes=(bytes KB MB GB TB)
progname=$(basename $0)
if [ -d ".mountscripts" ]; then
scriptdir="$(pwd)/.mountscripts"
elif [ -d ".${progname}-mountscripts" ]; then
scriptdir="$(pwd)/.${progname}-mountscripts"
fi
if ([[ ! -d "${scriptdir}" ]] && [[ "$1" != "s" ]]) || [[ "$1" == "h" ]]; then
cat << EOF
usage: $0 [s] [t]
s = scan
t = test run (USE THIS THE FIRST TIME AFTER YOU HAVE DONE A SCAN)
EOF
exit 1
fi
if [[ "$1" = "s" ]]; then
echo -n "Scanning disk tables... (by name)"
parttable=(); while IFS= read -r line; do parttable+=("$line"); done < \
<(find /dev/disk/by-id/ -name "scsi-SATA*" -name "*-part*" -type l | xargs stat -L -c "%t-%T %n")
echo -n ", (by UUID)"
uuidtable=(); while IFS= read -r line; do uuidtable+=("$line"); done < \
<(find /dev/disk/by-uuid/ -type l | xargs stat -L -c "%t-%T %n")
echo -ne " [ok]\nRunning blkid..."
blkidtable=(); while IFS= read -r line; do blkidtable+=("$line"); done < \
<(blkid)
echo -ne " [ok]\nRunning lsblk (uno momento)..."
lsblktable=(); while IFS= read -r line; do lsblktable+=("$line"); done < \
<(lsblk -bro name,size,fstype,model | grep -v group | tail -n +2)
echo -e " [ok]\n"
if [ ${#parttable[@]} -ne ${#uuidtable[@]} ]; then
echo 'Something is very wrong with either this program'
echo 'or your disk configuration. O.o'
exit 1
fi
echo -e "Using scriptdir \"${scriptdir}\".\n"
echo -ne "\e[1GCompiling mapping table... [ ]\e[?25l"
max=$[${#parttable[@]}*${#parttable[@]}]
runindex=0
for ((i = 0; i < "${#parttable[@]}"; i++)); do
partsplit=(${parttable[$i]})
devok=0
devname="$(readlink -f ${partsplit[1]})"
partsize=
for uuid in "${uuidtable[@]}"; do
uuidsplit=($uuid)
c=$[((runindex*43)/$[max-1])]
echo -ne "\e[29G"
if [ $c -gt 0 ]; then eval \printf "%.s#" {0..$c}; else echo -n '.'; fi
if [ $c -lt 43 ]; then eval \printf "%.s." {$[c+1]..43}; fi
((runindex++))
if [[ "${partsplit[0]}" = "${uuidsplit[0]}" ]]; then
partlabel=
devok=1
for entry in "${blkidtable[@]}"; do
if [[ "${entry:0:$[${#devname}+9]}" != "${devname}: LABEL=\"" ]]; then continue; fi
partlabel="${entry:$[${#devname}+9]}"
partlabel=$(echo -n $(echo $partlabel | cut -d'"' -f1))
done
for entry in "${lsblktable[@]}"; do
entry=($entry)
if [[ "/dev/${entry[0]}" != "$devname" ]]; then continue; fi
partsize=${entry[1]}
parttype=${entry[2]}
done
if [ ! partsize ]; then
echo "$0: error: cannot determine partition size for $devname"
exit 1
fi
devline="${partsplit[1]:26} ${uuidsplit[1]:18} ${partsize} ${parttype}${partlabel:+ $partlabel}"
map[${#map[@]}]="$devline"
fi
done
if [ $devok -eq 0 ]; then
checkparttable[${#checkparttable[@]}]="${parttable[$i]#* }"
fi
done
echo -e "\e[?25h\e[75Gdone.\n"
if [[ ${#checkparttable[@]} -gt 0 ]]; then
cat << EOF
Warning: The following partitions do not have matching UUID entries
in /dev/disk/by-uuid/.
Linux seems to be quite smart, and won't list UUIDs for LVM
members, partitions \`mount' cannot mount without the -t flag,
or extended partition headers, but /dev/disk/by-id/ will still
list them. So these are probably not a problem but may still
warrant a double-check; if these contain valid filesystems you
will need to insert them manually since their UUIDs cannot be
calculated.
EOF
for partition in "${checkparttable[@]}"; do
echo " >> $(readlink -f $partition) (/dev..by-id/${partition:26})";
done
echo
fi
find /dev/disk/by-id/ -name "scsi-SATA*" -not -name "*-part*" -type l | while read disk; do
scriptfile="${scriptdir}/${disk:26}.mount.sh"
rm -f "${scriptfile}"
if [ ! -f "${scriptfile}" ]; then
echo -ne "No mountscript found for disk ID \"${disk:26}\", creating one...\nRunning cfdisk... "
cfdtable=(); while IFS= read -r line; do cfdtable+=("$line"); done < \
<(cfdisk -Ps $disk | grep -v "Free Space" | grep -v "Unusable" | tail -n +6)
echo -ne "[ok]\nRunning smartctl... "
smartctlinfo="$(smartctl -i $disk)"
diskdevname="$(readlink -f ${disk})"
diskdevname=${diskdevname:5}
disk="${disk:26}"
disktable[${#disktable[@]}]="${disk}"
tmp=
diskparttable=
for entry in "${lsblktable[@]}"; do
entry=($entry)
if [[ "${diskdevname}" != "${entry[0]}" ]]; then continue; fi
devicename=$(echo -n $(echo "${entry[@]}" | cut -d' ' -f3-))
done
echo -e "# Script generated by domount at $(date +'%T on %D (MM/DD/YY)') for disk \"${devicename}\"\n" > "${scriptfile}"
echo '# '$(echo "$smartctlinfo" | grep '^Model Family:') >> "${scriptfile}"
echo '# '$(echo "$smartctlinfo" | grep '^Device Model:') >> "${scriptfile}"
echo -e '# '$(echo "$smartctlinfo" | grep '^User Capacity:')"\n" >> "${scriptfile}"
for part in "${map[@]}"; do
if [[ "${part:0:$[${#disk}+1]}" != "${disk}-" ]]; then continue; fi
diskparttable="${diskparttable}${part}\n";
done
mapfile -t diskparttable < <(echo -ne "${diskparttable%%\\n}" | sort -n -k1.$[${#disk}+6]n)
echo -ne "if diskexists ${disk}; then\n\t\n" >> "${scriptfile}"
for part in "${diskparttable[@]}"; do
partsplit=($part)
parttype=
for line in "${cfdtable[@]}"; do
line=($line)
if [[ "X${partsplit[0]:${#disk}+5}X" != "X${line[0]}X" ]]; then continue; fi
parttype="${line[1]}"
done
if [[ "X${parttype}X" = "XX" ]]; then
echo "$0: error: Cannot parse cfdisk output"
rm -f "${scriptfile}"
exit 1
fi
echo -ne "\t# Partition: #${partsplit[0]:${#disk}+5} (${parttype}, ${partsplit[3]}" >> "${scriptfile}"
if [[ "${partsplit[3]}" = "swap" ]]; then
echo -n " - Skipping" >> "${scriptfile}"
fi
echo -n "); Size: " >> "${scriptfile}"
sizeidx=0
size=${partsplit[2]}
while [ $size -gt 0 ]; do
sizetext="${size}${sizes[$sizeidx]} ${sizetext}"
size=$(($size/1024))
((sizeidx++))
done
sizetext=($sizetext)
for ((i = 0; i < 2; i++)); do
if [ $i -eq 1 ]; then echo -n ' (' >> "${scriptfile}"; fi
if [[ "${sizetext[$i]: -1:1}" = "s" ]]; then
echo -n "${sizetext[$i]:0:-5} bytes" >> "${scriptfile}"
else
echo -n "${sizetext[$i]:0:-2} ${sizetext[$i]: -2:2}" >> "${scriptfile}"
fi
if [ $i -eq 1 ]; then echo -n ')' >> "${scriptfile}"; fi
done
if [[ "X${partsplit[4]}X" != "XX" ]]; then
echo -n "; Label: \"" >> "${scriptfile}"
echo $(echo -n "${part}" | cut -d' ' -f5-)"\"" >> "${scriptfile}"
else
echo >> "${scriptfile}"
fi
if [[ "${partsplit[3]}" != "swap" ]]; then
echo -e "\tmountpart /dev/disk/by-uuid/${partsplit[1]} \n\t" >> "${scriptfile}"
else
echo -e "\t" >> "${scriptfile}"
fi
done
echo "fi" >> "${scriptfile}"
echo -e "[ok]\nSuccess!\n"
#echo ---; cat $scriptfile; echo ---
else
echo "Script found for disk ID ${disk}"
fi
done
exit
fi
trap 'echo; exit' SIGINT
echo -ne "Using scriptdir \"${scriptdir}\".\nRunning mount..."
mapfile -t mounttable < <(mount)
echo -e " [ok]"
function spin() {
trap 'echo -e "\e[?25h"' SIGINT SIGQUIT SIGKILL
echo -ne "\e[?25l"
if [[ $unicode -eq 1 ]]; then s=$(printf \\u2580\\u259C\\u2590\\u259F\\u2584\\u2599\\u258C\\u259B); m=8; d=0.03; else s='/-\|'; m=4; d=0.07; fi
("$@" & pid=$! ; c=1; while ps -c $pid 2>&1>/dev/null; do echo -ne "\e[s${s:c:1} \e[u"; c=$[c+1]; test $c -eq $m && c=0; sleep $d; done)
echo -ne "\e[?25h"
trap SIGINT SIGQUIT SIGKILL
}
function diskexists {
disk=/dev/disk/by-id/scsi-SATA_${@}
if [[ ! -L $disk ]]; then
echo "(Disk $0 is not installed)"
else
echo "[Disk ${1}]"
fi
}
function partop {
if [[ $mode -eq 1 ]]; then
while true; do
echo -n "Unmounting ${1##*/}... "
if ! mountpoint > /dev/null 2>&1 $2; then
echo "(Not mounted, or not a mountpoint)"
break;
fi
if [ ! -d $2 ]; then
echo "error: Not a directory!"
break
fi
cmd="umount $1"
if [[ ! $testmode ]]; then
output="$(${cmd} 2>&1)"
err=$?
else
echo "{would run: ${cmd}} "
fi
if [[ $err = 0 ]]; then
if [[ ! $testmode ]]; then echo "[ok]"; fi
return
else
echo -e "\n== umount error =="
echo -n "${output}"
echo -e "\n=================\n"
c=X;
while [[ ! $c =~ (R|r|P|p|D|d|Q|q) ]]; do read -sn1 -p"[R]etry/Skip [P]artition/Skip [D]isk/[Q]uit? " c; echo $c; done
echo
case $c in
D|d) skipdisk=1; break ;;
P|p) break ;;
Q|q) exit ;;
esac
fi
done
else
if [[ $skipdisk = 1 ]] && [[ $newdisk = 0 ]]; then return; fi
err=0
skipdisk=0
newdisk=0
while true; do
echo -n "${1##*/} -> $2: "
if mountpoint > /dev/null 2>&1 $2; then
echo "(already mounted)"
break;
fi
if [[ $testmode == 0 ]]; then echo echo -n "Mounting"; fi
if [ ! -d $2 ]; then
echo -n " (creating dir $2"
cmd="mkdir -p $2 2>&1"
if [[ ! $testmode ]]; then
output="$(eval $cmd)"
err=$?
else
echo -n " {would run: $cmd}"
fi
echo -n ') '
fi
if [[ $err = 0 ]]; then
if [[ $testmode == 0 ]]; then echo -n '... '; fi
cmd="mount $@"
if [[ ! $testmode ]]; then
output="$(${cmd} 2>&1)"
err=$?
else
echo "{would run: ${cmd}} "
fi
else
echo
fi
if [[ $err = 0 ]]; then
if [[ ! $testmode ]]; then echo "[ok]"; fi
return
else
echo -e "\n== mount error =="
echo -n "${output}"
echo -e "\n=================\n"
c=X;
while [[ ! $c =~ (R|r|P|p|D|d|Q|q) ]]; do read -sn1 -p"[R]etry/Skip [P]artition/Skip [D]isk/[Q]uit? " c; echo $c; done
echo
case $c in
D|d) skipdisk=1; break ;;
P|p) break ;;
Q|q) exit ;;
esac
fi
done
fi
}
if [[ $1 = "u" ]]; then mode=1; else mode=0; fi
if [[ $1 = "t" ]]; then testmode=1; fi
scripts=(${scriptdir}/*.mount.sh)
for ((i = 0; i < ${#scripts[@]}; i++)); do
newdisk=1
. ${scripts[$i]}
if (($i < ${#scripts[@]} - 1)); then echo; fi
done
echo -ne "\e[?25h"
Hopefully someone else finds this helpful. I am aware of udev/automount; that was overkill, since the disks are always installed, and I don't need a system whose focus is on-the-fly detection of newly inserted media of whatever kind.
-i336
Last edited by i336 (2011-11-10 04:42:08)
Offline
Thanks. I might use it soon...
Does it automatically make folders named after the volume labels? And does it handle the conversion of spaces and non-alphanumeric characters to octal codes?
I could read the script but it would be faster for everyone reading, if you leave the answer as a reply.
I also think that there should be some major work done on modernizing the fstab, either by replacing it with a better implementation of file system mounting or changing the file structure and adding in better handling of non-alphanumerics. I don't want to have to look up a stupid octal table every time I type in my labels.
Offline