Iterating over directories containing a string shell script - shell

I have a set of directories, 10 at the moment that are named client-1, client-2,..., client-10 and 1 directory that is named nestjs-wrapper
I want to iterate over the client directories, enter each of them and fire npm install and node index.js in every one.
I could do it by hand, but the number of clients may increment in the future so I would like to automate this process.
So the flow would be something like this:
in the parent directory I would like to fire nvm use to make sure I have the desired node version
then cd into each directory, fire npm install & node index.js
cd back to parent directory
repeat this until packages are installed in every client directory
run docker-compose up in a detached terminal
cd from parent directory into a nestjs-wrapper and start it in watch mode with npm run start:dev
This is the start of the attempt, it installs the packages in the client directories, now I would somehow need to do the rest of the flow:
pattern="/home/dario/my-folder/client"
for _dir in "${pattern}-"[[:digit:]]*; do
[ -d "$_dir" ] || continue;
pushd "$_dir" && npm install;
done
I would like to start docker-compose from the parent directory in a detached terminal.
To do this, I just created a new script named start-docker.sh in which I only have docker-compose up.
And after that open a separate dir in the parent directory (one that is not named client-) and run npm run start:dev in it.
So it would go something like:
pattern="client"
for _dir in "${pattern}-"[[:digit:]]*; do
[ -d "$_dir" ] || continue;
pushd "$_dir" && npm install && node index.js;
popd;
done
gnome-terminal -- ./start-docker.sh;
pushd nestjs_wrapper && npm run start:dev;
This does the trick, I switched back to relative pathnames. First I iterate over all the client directories and install the packages, then after that I bring up docker-compose and start the wrapper in watch mode.

Following the input from the comments, here is the working solution:
pattern="client"
for _dir in "${pattern}-"[[:digit:]]*; do
[ -d "$_dir" ] || continue;
pushd "$_dir" && npm install && node index.js;
popd;
done
gnome-terminal -- ./start-docker.sh;
pushd nestjs_wrapper && npm run start:dev;

Related

Defining a loop within a script inside package.json

Is it possible to include a for loop inside a package.json file to recurse into subdirectories and execute a command in each:
# .env File
DIRECTORIES="Dir1, Dir2"
# package.json file in parent directory
"scripts": {
"install-all-inefficient": "(cd ./dir/Dir1/ && npm install) && (cd ./dir/Dir2/ && npm install)",
"install-all-ideal": ". .env && functionToLoopThroughDirectoriesDefinedInEnvironmentVariables",
},
I was looking at this: Defining an array as an environment variable in node.js as an example for defining the array, and then trying to somehow use process.env.DIRECTORIES to create a loop without writing too much ugly bash script.
You can try :
"install-all-ideal": ". .env; for dir in \"${DIRECTORIES[#]}\"; do (cd \"$dir\" && npm install); done"
with .env :
DIRECTORIES=("Dir1" "Dir2")

How can I go to path and execute a 'node 'command in bash as one-liner?

Usually I do this:
cd $HOME
cd Desktop
cd project_folder
node server.js
Now it's time to increase efficicency and do one-liner.
I want to do this:
cd $HOME/Desktop/project_folder node server.js
How do I go to a path AND execute a command?
Use &&:
cd $HOME/Desktop/project_folder && node server.js
You can find more info here: https://www.tldp.org/LDP/abs/html/ops.html

How to run set of terminal commands in a file one by one?

I was working on a Django project and each time I open the project, I have to run these three commands one by one.
source virtualenv/bin/activate (to activate virtual environment)
cd myproject (to enter project folder)
python3 manage.py runserver (to run the server)
Is there a way I can write these three commands in a text file (or some other file) so that a single command will run these three commands one by one?
Before running those three commands, terminal looks like this:
After running those three commands, the terminal should look like this:
Try this:
In file runscript.sh,
#!/bin/bash
source virtualenv/bin/activate
cd myproject
python3 manage.py runserver
Then run command: . runscript.sh instead of ./runscript.sh
Save following content to a file runserver.sh
#!/bin/bash
source virtualenv/bin/activate && cd myproject && python3 manage.py runserver
and
chmod 744 runserver.sh
And you can just run ./runserver.sh
&& will ensure previous command was successful before running next command. If any command fails to run, it will abort rest.

How to check if npm script exists?

I am creating a bash script which runs through each of my projects and runs npm run test if the test script exists.
I know that if I get into a project and run npm run it will give me the list of available scripts as follows:
Lifecycle scripts included in www:
start
node server.js
test
mocha --require #babel/register --require dotenv/config --watch-extensions js **/*.test.js
available via `npm run-script`:
dev
node -r dotenv/config server.js
dev:watch
nodemon -r dotenv/config server.js
build
next build
However, I have no idea how to grab that information, see if test is available and then run it.
Here is my current code:
#!/bin/bash
ROOT_PATH="$(cd "$(dirname "$0")" && pwd)"
BASE_PATH="${ROOT_PATH}/../.."
while read MYAPP; do # reads from a list of projects
PROJECT="${MYAPP}"
FOLDER="${BASE_PATH}/${PROJECT}"
cd "$FOLDER"
if [ check here if the command exists ]; then
npm run test
echo ""
fi
done < "${ROOT_PATH}/../assets/apps-manifest"
EDIT:
As mentioned by Marie and James if you only want to run the command if it exists, npm has an option for that:
npm run test --if-present
This way you can have a generic script that work with multiple projects (that may or may not have an specific task) without having the risk of receiving an error.
Source: https://docs.npmjs.com/cli/run-script
EDIT
You could do a grep to check for the word test:
npm run | grep -q test
this return true if the result in npm run contains the word test
In your script it would look like this:
#!/bin/bash
ROOT_PATH="$(cd "$(dirname "$0")" && pwd)"
BASE_PATH="${ROOT_PATH}/../.."
while read MYAPP; do # reads from a list of projects
PROJECT="${MYAPP}"
FOLDER="${BASE_PATH}/${PROJECT}"
cd "$FOLDER"
if npm run | grep -q test; then
npm run test
echo ""
fi
done < "${ROOT_PATH}/../assets/apps-manifest"
It just would be a problem if the word test is in there with another meaning
Hope it helps
The right solution is using the if-present flag:
npm run test --if-present
--if-present doesn't allow you to "check if a npm script exists", but runs the script if it exists. If you have fallback logic this won't suffice. In my case, I want to run npm run test:ci if it exists and if not check for and run, npm run test. Using --if-present would run the test:ci AND test scripts if both exists. By checking if one exists first, we can decide which to run.
Because I have both "test" and "test:ci" scripts, the npm run | grep approach wasn't sufficient. As much as I'd like to do this with strictly npm, I have jq in my environments so I decided to go that route to have precision.
To check for a script named "test:ci":
if [[ $(jq '.scripts["test:ci"]' < package.json;) != null ]]; then
// script exists
fi

How to make shell script run each command asynchronously

I want to do an npm install in a ton of directories.
Can I create a shell script that will run npm install in all of them asynchronously? So I don't have to wait long for all of them to be done?
IE
cd foo; npm install; cd ..;
cd bar; npm install; cd ..;
etc.
You can run them in the background using & at the end:
cd foo && npm install &
cd bar && npm install &
There's no need for cd .. here because each line runs in a separate child process.
Also I'm using && here instead of ;, otherwise you'd need to add () to group the commands:
( cd foo; npm install ) &
( cd bar; npm install ) &
As a plus, && will not execute commands to it's right if the command to it's left fails.

Resources