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

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

The following answer was given by SE user Oh My Goodness. The original source can be found here. Instead of cat "$x" | command or echo "$x" | command, use command <$x (vs cat) or command ...

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

Answer
#2: Post edited by user avatar aura-lsprog-86‭ · 2025-02-15T06:44:25Z (6 days ago)
Reformat code blocks inside bullet points.
  • _The following answer was given by SE user [Oh My Goodness](https://codereview.stackexchange.com/users/131732). The original source can be found [here](https://codereview.stackexchange.com/a/210264/98306)._
  • <hr />
  • * Instead of `cat "$x" | command` or `echo "$x" | command`, use `command <$x` (vs `cat`) or `command <<<$x` (vs `echo`): it saves a fork and removes the need to quote.
  • * Instead of `if [ x -lt y ]` use `if [[ x -lt y ]]`: it saves a fork (`[[` is a bash builtin; `help test` for details) and adds some functionality.
  • * Functions return their last exit value already so `contains()` can be shortened to the following (whether you prefer this is up to you):
  • ```
  • contains() {
  • test "${1#*$2}" != "$1"
  • }
  • ```
  • * Use bash defaulting mechanism instead of `if [[ -z`, as in `CONF=${2:-./steps.json}`.
  • * Use `for ((i=0; i<$LIMIT; i++))` instead of `i=0; while ...`.
  • * Test the exit values of things that shouldn't fail, as in `mkdir -p "$DESTROOT" || exit 1`. **Any invocation of `cd` or `pushd` should be checked for success, always!** A general purpose `DIE()` function can replace the naked exit and take an error message as an argument. If nothing should fail, `set -e` or `trap DIE ERR` (the first argument is a function name) does this globally.
  • * Constructions like `jq -r ".["$i"].files | length")` and `echo " ""$FSRC"` are kind of weird and the inner double quotes probably should be removed.
  • * In a language where every variable is a global, it's a good habit to use fewer variables. For example, `RES=$(foo); LOOP=$( echo "$RES" | ...)` can just be `LOOP=$( foo | ...)`.
  • * Your get-conf pattern should be in a function like:
  • ```
  • get_conf() {
  • jq -r $1<<<$CONF
  • }
  • ```
  • * Pruning code paths is important in an interpreted language. Since the wildcard copy method works for regular copies too, just use that one unconditionally and remove `if contains ... "\*"`.
  • * You don't need to escape wildcards like `*` in double quotes. When in doubt about what will be interpolated, use single quotes. Quoting in bash can be very complex and take a long time to learn; an advanced understanding of it will help to avoid common bugs.
  • * Since you are using commands that aren't standard, it's a good idea to set `PATH` in the script, or as an optional config directive, and to check that they're there before you begin, as in the following example:
  • ```
  • require() {
  • for cmd in "$@"; do
  • type $cmd >/dev/null || exit 1
  • done
  • }
  • # Code here...
  • require jq udisksctl
  • ```
  • * Read `CONF` just once, into a variable: `conf=$(<$CONF)`, and query that. Then you can edit the config while the script runs.
  • _The following answer was given by SE user [Oh My Goodness](https://codereview.stackexchange.com/users/131732). The original source can be found [here](https://codereview.stackexchange.com/a/210264/98306)._
  • <hr />
  • * Instead of `cat "$x" | command` or `echo "$x" | command`, use `command <$x` (vs `cat`) or `command <<<$x` (vs `echo`): it saves a fork and removes the need to quote.
  • * Instead of `if [ x -lt y ]` use `if [[ x -lt y ]]`: it saves a fork (`[[` is a bash builtin; `help test` for details) and adds some functionality.
  • * Functions return their last exit value already so `contains()` can be shortened to the following (whether you prefer this is up to you):
  • contains() {
  • test "${1#*$2}" != "$1"
  • }
  • * Use bash defaulting mechanism instead of `if [[ -z`, as in `CONF=${2:-./steps.json}`.
  • * Use `for ((i=0; i<$LIMIT; i++))` instead of `i=0; while ...`.
  • * Test the exit values of things that shouldn't fail, as in `mkdir -p "$DESTROOT" || exit 1`. **Any invocation of `cd` or `pushd` should be checked for success, always!** A general purpose `DIE()` function can replace the naked exit and take an error message as an argument. If nothing should fail, `set -e` or `trap DIE ERR` (the first argument is a function name) does this globally.
  • * Constructions like `jq -r ".["$i"].files | length")` and `echo " ""$FSRC"` are kind of weird and the inner double quotes probably should be removed.
  • * In a language where every variable is a global, it's a good habit to use fewer variables. For example, `RES=$(foo); LOOP=$( echo "$RES" | ...)` can just be `LOOP=$( foo | ...)`.
  • * Your get-conf pattern should be in a function like:
  • get_conf() {
  • jq -r $1<<<$CONF
  • }
  • * Pruning code paths is important in an interpreted language. Since the wildcard copy method works for regular copies too, just use that one unconditionally and remove `if contains ... "\*"`.
  • * You don't need to escape wildcards like `*` in double quotes. When in doubt about what will be interpolated, use single quotes. Quoting in bash can be very complex and take a long time to learn; an advanced understanding of it will help to avoid common bugs.
  • * Since you are using commands that aren't standard, it's a good idea to set `PATH` in the script, or as an optional config directive, and to check that they're there before you begin, as in the following example:
  • require() {
  • for cmd in "$@"; do
  • type $cmd >/dev/null || exit 1
  • done
  • }
  • # Code here...
  • require jq udisksctl
  • * Read `CONF` just once, into a variable: `conf=$(<$CONF)`, and query that. Then you can edit the config while the script runs.
#1: Initial revision by user avatar aura-lsprog-86‭ · 2025-02-15T06:31:51Z (6 days ago)
_The following answer was given by SE user [Oh My Goodness](https://codereview.stackexchange.com/users/131732). The original source can be found [here](https://codereview.stackexchange.com/a/210264/98306)._

<hr />

* Instead of `cat "$x" | command` or `echo "$x" | command`, use `command <$x` (vs `cat`) or `command <<<$x` (vs `echo`): it saves a fork and removes the need to quote.
* Instead of `if [ x -lt y ]` use `if [[ x -lt y ]]`: it saves a fork (`[[` is a bash builtin; `help test` for details) and adds some functionality.
* Functions return their last exit value already so `contains()` can be shortened to the following (whether you prefer this is up to you):

```
    contains() {
        test "${1#*$2}" != "$1"
    }
```

* Use bash defaulting mechanism instead of `if [[ -z`, as in `CONF=${2:-./steps.json}`.
* Use `for ((i=0; i<$LIMIT; i++))` instead of `i=0; while ...`.
* Test the exit values of things that shouldn't fail, as in `mkdir -p "$DESTROOT" || exit 1`. **Any invocation of `cd` or `pushd` should be checked for success, always!** A general purpose `DIE()` function can replace the naked exit and take an error message as an argument. If nothing should fail, `set -e` or `trap DIE ERR` (the first argument is a function name) does this globally.
* Constructions like `jq -r ".["$i"].files | length")` and `echo "    ""$FSRC"` are kind of weird and the inner double quotes probably should be removed.
* In a language where every variable is a global, it's a good habit to use fewer variables. For example, `RES=$(foo); LOOP=$( echo "$RES" | ...)` can just be `LOOP=$( foo | ...)`.
* Your get-conf pattern should be in a function like:

```
    get_conf() {
        jq -r $1<<<$CONF
    }
```

* Pruning code paths is important in an interpreted language. Since the wildcard copy method works for regular copies too, just use that one unconditionally and remove `if contains ... "\*"`.
* You don't need to escape wildcards like `*` in double quotes. When in doubt about what will be interpolated, use single quotes. Quoting in bash can be very complex and take a long time to learn; an advanced understanding of it will help to avoid common bugs.
* Since you are using commands that aren't standard, it's a good idea to set `PATH` in the script, or as an optional config directive, and to check that they're there before you begin, as in the following example:

```
    require() {
        for cmd in "$@"; do
            type $cmd >/dev/null || exit 1
        done
    }

    # Code here...

    require jq udisksctl
```

* Read `CONF` just once, into a variable: `conf=$(<$CONF)`, and query that. Then you can edit the config while the script runs.