skip to Main Content

I would like a bash shell script (for my Debian 12 system) which accepts multiple paths as arguments, and then checks if a file can be successfully created in each one. Then the result should be put into json format.

The command to run should be:

./checkavailable.sh /the/first/path /the/second/path

(where there could be any number of input paths given, but at least one)
and I would like the output to be in the following format:

[
  { "is_available": 0, "path": "/the/first/path/" },
  { "is_available": 1, "path": "/the/second/path/" }
]

Currently, I managed to achieve this with the following script:

#!/bin/bash

# Check if first path is available
if touch $1/checkalivefile ; then
    avail_path1=1
    rm $1/checkalivefile
else
    avail_path1=0
fi

# Check if second path is available
if touch $2/checkalivefile ; then
    avail_path2=1
    $2/checkalivefile
else
    avail_path2=0
fi

echo "["
printf "  { "is_available": "$avail_path1", "path": "$1" } "
printf ",n"
printf "  { "is_available": "$avail_path2", "path": "$2" } "
echo
echo "]"

but it’s obviously a bit primitive and is hardcoded to work with x2 arguments only, and doesn’t scale to additional arguments.

I could try to add a loop now. However, I came across this script, which seems to do something similar (to get disk usage in this case) but using awk instead of a loop:

#!/bin/bash

echo "["
du -s -B1 "$@" | awk '{if (NR!=1) {printf ",n"};printf "  { "dir_size_bytes": "$1", "path": ""$2"" }";}'
echo
echo "]"

Can someone show how this approach using "awk" could be used in my case? I am very new to bash scripts, so hope to learn something.

2

Answers


  1. Note that touch can take multiple arguments, so touch "$@" would already perform that command on all paths provided to your script. Depending on what is_available actually stands for in your desired output, you could then just query if the files are actually present, are writeable, etc.

    Here’s an approach that still iterates over your script’s arguments using a for loop, applies touch individually, and checks its (negated) exit code $?. These are then fed into a simple jq script that takes care of returning a valid JSON encoding, regardless of any strange characters in the paths provided.

    #!/bin/sh
    
    for path; do ! touch "$path/checkalivefile"; echo $?; done |
    jq -n --args '[$ARGS.positional[] | {is_available: input, path: .}]' "$@"
    

    Note: Swallow stderr by using touch … 2>/dev/null to silence the error messages printed on failure.

    Login or Signup to reply.
  2. We have two operations … 1) run a sample touch and 2) pretty print some output. Running the touch from within awk is unnecessary overhead, and calling an awk script to just pretty print what we can easily do in bash is overkill (imo). So, I would opt to do the whole thing in bash.

    General approach:

    • loop through list of paths
    • if we can successfully touch a file then:
    • a) rm the file
    • b) conditionally print the leading '['
    • c) print the is_available line (preface with ending for previous line)
    • d) conditionally print trailing ']'
    • NOTE: ending for previous line takes care of printing the ending , for all but the last line

    One bash idea:

    $ cat gen.jq
    #!/bin/bash
    
    count=0
    pfx=""                                     # 1st 'is_available' line prefaced with nothing
    
    for dir in "$@"
    do
        tgt="${dir}/.checkalivefile"
    
        if touch "${tgt}" 2>/dev/null
        then
            rm -f "${tgt}"
    
            (( count == 0 )) && echo "["       # if this is the 1st time printing an 'is_available'
                                               # line then print the opening '['
    
            printf "%b  { "is_available": %s, "path": "%s" }" "${pfx}" "${count}" "${dir}"
    
            (( count++ ))
    
            pfx=",n"                          # 2nd-nth 'is_available' lines prefaced with ','
                                               # to close out previous line + linefeed
        fi
    done
    
    (( count > 0 )) && printf "n]n"          # if we printed at least one 'is_available' line
                                               # so close out the last line (n) and print the
                                               # closing ']'
    

    Setup:

    $ cd /tmp
    $ mkdir -p dir1 dir1/subdir1 dir1/subdir2
    $ chmod 000 dir1/subdir2
    
    $ tree /tmp
    /tmp
    ├── dir1
    │   ├── subdir1
    │   └── subdir2  [error opening dir]
    

    Running the script:

    $ ./gen.jq /tmp/dir1 /tmp/dir1/subdir1 /tmp/dir1/subdir2 /tmp/dir2 
    [
      { "is_available": 0, "path": "/tmp/dir1" },
      { "is_available": 1, "path": "/tmp/dir1/subdir1" }
    ]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search