Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Code Reviews

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

66%
+2 −0
Code Reviews Serial copying from disk images to folder in Bash

(Brought over from SE.) 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 ...

2 answers  ·  posted 6d ago by aura-lsprog-86‭  ·  edited 6d ago by aura-lsprog-86‭

#3: Post edited by user avatar aura-lsprog-86‭ · 2025-02-15T19:44:11Z (6 days ago)
Change ownership of reviewed script
  • _(Brought over from [SE](https://codereview.stackexchange.com/q/209507/98306).)_
  • <hr />
  • 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.
  • <hr />
  • ## 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:
  • 1. which disk images will be opened,
  • 2. which files inside each disk image will be copied, and
  • 3. 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 in 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:
  • * The JSON parsing program `jq`.
  • * The disk image manipulation utility `udisksctl`.
  • To install these dependencies, use one of the following commands:
  • * Ubuntu
  • $ sudo apt install jq udisks2
  • * Fedora
  • $ sudo dnf install jq udisks2
  • ## Script
  • The complete script is below. It can be marked as executable to avoid having to prepend `bash` to its execution command. There is no restrictions on directories this script may reside into. For the purpose of the test below, the directory where it resides has read/write permissions.
  • ### `imgdisk-copy.sh`
  • #!/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](https://winworldpc.com/download/1ce2809c-2b4c-e280-9804-11c3a6e28094). 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 is also placed in the same directory the script is for convenience.
  • ### `steps.json`
  • [
  • {
  • "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
  • _(Brought over from [SE](https://codereview.stackexchange.com/q/209507/98306).)_
  • <hr />
  • 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.
  • <hr />
  • ## 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:
  • 1. which disk images will be opened,
  • 2. which files inside each disk image will be copied, and
  • 3. 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 in 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:
  • * The JSON parsing program `jq`.
  • * The disk image manipulation utility `udisksctl`.
  • To install these dependencies, use one of the following commands:
  • * Ubuntu
  • $ sudo apt install jq udisks2
  • * Fedora
  • $ sudo dnf install jq udisks2
  • ## Script
  • The complete script is below. It can be marked as executable to avoid having to prepend `bash` to its execution command. There is no restrictions on directories this script may reside into. For the purpose of the test below, the directory where it resides has read/write permissions.
  • ### `imgdisk-copy.sh`
  • #!/bin/bash
  • # Copying files contained inside disk images via JSON recipe.
  • # Aura Lesse Programmer
  • # 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](https://winworldpc.com/download/1ce2809c-2b4c-e280-9804-11c3a6e28094). 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 is also placed in the same directory the script is for convenience.
  • ### `steps.json`
  • [
  • {
  • "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
#2: Post edited by user avatar aura-lsprog-86‭ · 2025-02-15T06:43:04Z (6 days ago)
Add Fedora dependency install command
  • _(Brought over from [SE](https://codereview.stackexchange.com/q/209507/98306).)_
  • <hr />
  • 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.
  • <hr />
  • ## 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:
  • 1. which disk images will be opened,
  • 2. which files inside each disk image will be copied, and
  • 3. 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 in 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:
  • * The JSON parsing program `jq`.
  • * The disk image manipulation utility `udisksctl`.
  • In Ubuntu, these programs can be installed with the following command:
  • $ sudo apt install jq udisks2
  • ## Script
  • The complete script is below. It can be marked as executable to avoid having to prepend `bash` to its execution command. There is no restrictions on directories this script may reside into. For the purpose of the test below, the directory where it resides has read/write permissions.
  • ### `imgdisk-copy.sh`
  • #!/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](https://winworldpc.com/download/1ce2809c-2b4c-e280-9804-11c3a6e28094). 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 is also placed in the same directory the script is for convenience.
  • ### `steps.json`
  • [
  • {
  • "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
  • _(Brought over from [SE](https://codereview.stackexchange.com/q/209507/98306).)_
  • <hr />
  • 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.
  • <hr />
  • ## 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:
  • 1. which disk images will be opened,
  • 2. which files inside each disk image will be copied, and
  • 3. 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 in 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:
  • * The JSON parsing program `jq`.
  • * The disk image manipulation utility `udisksctl`.
  • To install these dependencies, use one of the following commands:
  • * Ubuntu
  • $ sudo apt install jq udisks2
  • * Fedora
  • $ sudo dnf install jq udisks2
  • ## Script
  • The complete script is below. It can be marked as executable to avoid having to prepend `bash` to its execution command. There is no restrictions on directories this script may reside into. For the purpose of the test below, the directory where it resides has read/write permissions.
  • ### `imgdisk-copy.sh`
  • #!/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](https://winworldpc.com/download/1ce2809c-2b4c-e280-9804-11c3a6e28094). 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 is also placed in the same directory the script is for convenience.
  • ### `steps.json`
  • [
  • {
  • "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
#1: Initial revision by user avatar aura-lsprog-86‭ · 2025-02-15T06:20:12Z (6 days ago)
Serial copying from disk images to folder in Bash
_(Brought over from [SE](https://codereview.stackexchange.com/q/209507/98306).)_

<hr />

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.

<hr />

## 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:
    1. which disk images will be opened,
    2. which files inside each disk image will be copied, and
    3. 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 in 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:

* The JSON parsing program `jq`.
* The disk image manipulation utility `udisksctl`.

In Ubuntu, these programs can be installed with the following command:

    $ sudo apt install jq udisks2

## Script

The complete script is below. It can be marked as executable to avoid having to prepend `bash` to its execution command. There is no restrictions on directories this script may reside into. For the purpose of the test below, the directory where it resides has read/write permissions.

### `imgdisk-copy.sh`

    #!/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](https://winworldpc.com/download/1ce2809c-2b4c-e280-9804-11c3a6e28094). 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 is also placed in the same directory the script is for convenience.

### `steps.json`

    [
        {
            "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