This is a Bash script that copies files stored inside disk images to a directory, using a defined structure provided via a JSON file. I've included the external programs it requires and the test I used so that you can test it too.
Any comments regarding programming style and improvements are welcome.
Overview
The following is a Bash shell script that copies files stored inside disk images into a directory in the filesystem.
The script takes two parameters:
- The first one is optional and defines a root directory (existing or not) that will contain the files being copied.
- The second one, optional when the first one is given, is a path to a valid JSON-formatted file that describes:
- which disk images will be opened,
- which files inside each disk image will be copied, and
- which path inside the directory root will be used as the destination for the files being copied.
The first parameter defaults to the current directory when not given. The second one defaults to a file named steps.json
located at the current directory. If the first parameter is not given, the second one can't be either.
Prerequisites
This script requires the following external programs to work correctly (installation instructions for Ubuntu are between parentheses):
- The JSON parsing program
jq
(sudo apt install jq
). - The disk image manipulation utility
udisksctl
(sudo apt install udisks2
)
Script
The complete script is below. It's name is imgdisk-copy.sh and should be marked as executable. It can be in any directory where it can be executed. For the purpose of the test below, it is placed in a directory where it can read and write.
#!/bin/bash
# Copying files contained inside disk images via JSON recipe.
# logo_writer
# December 12th, 2018
# Is a string contained in another? Return 0 if so; 1 if not.
# By fjarlq, from https://stackoverflow.com/a/8811800/5397930
contains() {
string="$1"
substring="$2"
if test "${string#*$substring}" != "$string"; then
return 0
else
return 1
fi
}
# Obtain the absolute path of a given directory.
# By dogbane, from https://stackoverflow.com/a/3915420
abspath() {
dir="$1"
echo "$(cd "$(dirname "$dir")"; pwd -P)/$(basename "$dir")"
}
# The main script starts here.
# If no first parameter is given, assume current directory.
if [ -z "$1" ]; then
DESTROOT="."
else
# Omit any trailing slash
DESTROOT=$(abspath "${1%/}")
fi
# If no second parameter is given, assume file "steps.json".
# If no first parameter is given, this can't be either.
if [ -z "$2" ]; then
CONF="./steps.json"
else
CONF="$2"
fi
# Create the root directory where the files will the put.
mkdir -p "$DESTROOT"
# How many disks will be processed?
LIMIT=$(cat "$CONF" | jq -r length)
i=0
while [ "$i" -lt "$LIMIT" ]; do
# For each disk, get its file name.
DISK=$(cat "$CONF" | jq -r .["$i"].disk)
echo "$DISK"
# Setup a loop device for the disk and get its name.
RES=$(udisksctl loop-setup -f "$DISK")
LOOP=$(echo "$RES" | cut -f5 -d' ' | head -c -2)
# Using the loop device obtained, mount the disk.
# Obtain the mount root directory afterwards.
RES=$(udisksctl mount -b "$LOOP")
SRCDIR=$(echo "$RES" | sed -nE 's|.*at (.*)\.|\1|p')
# How many file sets will be copied?
NOITEMS=$(cat "$CONF" | jq -r ".["$i"].files | length")
j=0
while [ "$j" -lt "$NOITEMS" ]; do
# For each file set, obtain which files will be copied and where.
FSRC=$(cat "$CONF" | jq -r .["$i"].files["$j"].src)
FDEST=$(cat "$CONF" | jq -r .["$i"].files["$j"].dest)
# Make the destination directory.
mkdir -p "$DESTROOT"/"$FDEST"
echo " ""$FSRC"
if contains "$FSRC" "\*"; then
# If a wildcard is used in the file set, copy by file expansion (option -t).
pushd "$SRCDIR" > /dev/null
cp -t "$DESTROOT"/"$FDEST" $FSRC
popd > /dev/null
else
# Else, copy normally.
cp "$SRCDIR"/"$FSRC" "$DESTROOT"/"$FDEST"
fi
j=$(($j + 1))
done
# Once all the file sets are copied, unmount the disk
# and delete its associated loop device.
udisksctl unmount -b "$LOOP" > /dev/null
udisksctl loop-delete -b "$LOOP"
i=$(($i + 1))
done
Test set
This script was tested with the following disk set: Microsoft C Compiler 4.0. The first 3 .img
disks inside the ZIP (disk01.img
, disk02.img
and disk03.img
) should be placed in the same directory the script is.
The corresponding JSON recipe used for the test is below. It's named steps.json and placed in the same directory the script is for convenience.
[
{
"disk": "disk01.img",
"files": [
{
"src": "*",
"dest": "bin"
}
]
},
{
"disk": "disk02.img",
"files": [
{
"src": "*.EXE",
"dest": "bin"
}
]
},
{
"disk": "disk03.img",
"files": [
{
"src": "LINK.EXE",
"dest": "bin"
},
{
"src": "*.H",
"dest": "include"
},
{
"src": "SYS/*.H",
"dest": "include/sys"
},
{
"src": "SLIBC.LIB",
"dest": "lib"
},
{
"src": "SLIBFP.LIB",
"dest": "lib"
},
{
"src": "EM.LIB",
"dest": "lib"
},
{
"src": "LIBH.LIB",
"dest": "lib"
}
]
}
]
The test is performed by opening a terminal and executing the following command:
./imgdisk-copy.sh testing/
The command will output each disk image name as it is mounted, and under it the names of the files being copied (unexpanded), as follows:
disk01.img
*
disk02.img
*.EXE
disk03.img
LINK.EXE
*.H
SYS/*.H
SLIBC.LIB
SLIBFP.LIB
EM.LIB
LIBH.LIB
The result will be a directory testing
under where the script is with the following structure:
testing/
├── bin
│ ├── C1.EXE
│ ├── C2.EXE
│ ├── C3.EXE
│ ├── CL.EXE
│ ├── CV.EXE
│ ├── EXEMOD.EXE
│ ├── EXEPACK.EXE
│ ├── LIB.EXE
│ ├── LINK.EXE
│ ├── MAKE.EXE
│ ├── MSC.EXE
│ └── SETENV.EXE
├── include
│ ├── sys
│ │ ├── LOCKING.H
│ │ ├── STAT.H
│ │ ├── TIMEB.H
│ │ ├── TYPES.H
│ │ └── UTIME.H
│ ├── ASSERT.H
│ ├── CONIO.H
│ ├── CTYPE.H
│ ├── DIRECT.H
│ ├── DOS.H
│ ├── ERRNO.H
│ ├── FCNTL.H
│ ├── FLOAT.H
│ ├── IO.H
│ ├── LIMITS.H
│ ├── MALLOC.H
│ ├── MATH.H
│ ├── MEMORY.H
│ ├── PROCESS.H
│ ├── SEARCH.H
│ ├── SETJMP.H
│ ├── SHARE.H
│ ├── SIGNAL.H
│ ├── STDARG.H
│ ├── STDDEF.H
│ ├── STDIO.H
│ ├── STDLIB.H
│ ├── STRING.H
│ ├── TIME.H
│ ├── V2TOV3.H
│ └── VARARGS.H
└── lib
├── EM.LIB
├── LIBH.LIB
├── SLIBC.LIB
└── SLIBFP.LIB