'Bash script that changes a JavaScript file based on user input

I want to create a shell command that runs bash commands but also mutates an array in a Javascript file. For example in the file groups.js:

module.exports = {
  groups: [
    "a",
    "b",
    "c"
  ]
}

I want to be able to run a shell command that would wait for user input, for example, +/-, and then will add/remove groups to that groups array in the groups.js file and also run echo "New group added successfully" at the end. Is this possible?

Example of code execution:

./command.sh
What would you like to do? (+ for add, - for remove): +
What group do you want to add: d
New groups:   
  groups: [
    "a",
    "b",
    "c",
    "d"
  ]
Successfully added "d" to groups.js! 


Solution 1:[1]

As mentioned in the comments, it may be a lot easier if people can edit the groups.js file directly, however, if a script must be used, this bash script can be a start.

$ cat command.sh
#!/usr/bin/env bash

file=groups.js

read -rp "What would you like to do? (+ for add, - for remove): " add_or_remove_group
if [[ "$add_or_remove_group" == "+" ]]; then
    cat "$file"
    read -rp "What group do you want to add: " add_group
    clear
    sed -i "s/\"[a-z]\"$/&,\n    \"$add_group\"/" "$file"
    cat "$file"
    echo "Successfully added \"$add_group\" to groups.js"
else
    cat "$file"
    read -rp "What group do you want to remove: " remove_group
    clear
    tac "$file"| sed "/\[\|{\|}/ ! {/$remove_group/d}" | tac | sed '/,/{N;N;/}/{s/,//}}' | tee "$file"
    echo "Successfully removed \"$remove_group\" to groups.js"
fi
$ ./command.sh
What would you like to do? (+ for add, - for remove): +
module.exports = {
  groups: [
    "a",
    "b",
    "c"
  ]
}
What group do you want to add: d

module.exports = {
  groups: [
    "a",
    "b",
    "c",
    "d"
  ]
}
Successfully added "d" to groups.js
$ ./command.sh
What would you like to do? (+ for add, - for remove): -
module.exports = {
  groups: [
    "a",
    "b",
    "c",
    "d"
  ]
}
What group do you want to remove: a

module.exports = {
  groups: [
    "b",
    "c",
    "d"
  ]
}
Successfully removed "a" to groups.js

Solution 2:[2]

#!/usr/bin/bash

Add_Group() {
eval "$(echo "sed 'H;\$!d;x;/module.exports[^{]*{/,/^}/ s/\n/@@##/g;s/^[^:]*:^[\[]*\[//g;s/\(groups[^\[]*\[\)\([^\\\"]*\)\(.*\\\"\)\([^\]]*\)/\1\2\3,\2\\\"$group\\\"\4/g;s/\(^.*\\\"\)\(.*\)/\n\1\n\2/;h;s/.*\n//;s/^,//g;G;s/\(.*\n\)\(.*\n\)\(.*\)/\2\1/;s/\n//g;s/@@##/\n/g' $File -i")"
}

Remove_Group() {
eval "$(echo "sed '/\\\"$group\\\"/d;H;\$!d;x;s/\n/@@##/g;s/\(^.*\\\"\)\(.*\)/\n\1\n\2/;h;s/.*\n//;s/^,//g;G;s/\(.*\n\)\(.*\n\)\(.*\)/\2\1/;s/\n//g;s/@@##/\n/g' $File -i")"
}

File=groups.js

if [[ "$#" -gt 0 ]]
then
if [[ "$1" == "--add" ]]
then
for group in "${@:2}"; 
do
Add_Group
done
echo "New Groups:"
cat $File
elif [[ "$1" == "--remove" ]];
then
for group in "${@:2}";
do
Remove_Group
done
echo "New Groups:"
cat $File
fi
else
echo 'What would you like to do? (+ for add, - for remove):'
read function
if [[ "$function" == "+" ]]; then
cat $File
read -p "Enter group names separated by 'space' that you want to add : " input
for group in ${input[@]}
do
Add_Group
done
echo "New Groups:"
cat $File
elif [[ "$function" == "-" ]]; then
cat $File
read -p "Enter group names separated by 'space' that you want to remove : " input
for group in ${input[@]}
do
Remove_Group
done
echo "New Groups:"
cat $File
fi
fi

This script can be used:

Interactively: ./Script.sh

NonInteractively:
./Script.sh --add group1 group2 group3 ..
./Script.sh --remove group1 group2 group3...

Note: Complexity of sed command used is to retain the structure and indents spaces in file without any constraints on regex as in the other answer. Hence if groups.js contains extra texts other than template shown it will work

Short Explanation of sed commands:
Add_Group
Loads the entire file in buffer --> For range /module.exports[^{]*{/,/^}/ ---> Replace \n with @### to restore structure at last ---> Adds comma to last group in existing ---> Adds new group ---> Replace the @@## back to \n

Remove_Group
--->Delete the line containing "group" ---> In case the group removed is the last existing then comma in last group after editing is removed restoring original structure

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2