There is a dedicated tool for this - s6-overlay.
To quote from their description:

A simple init process which allows the end-user to execute tasks like initialization (...) Multiple processes in a single container (...) Able to operate in "The Docker Way"

The repo provides lengthy explanation how it works, how to install etc. which I won't repeat here.

Example

Imo their repo lacks a working, straightforward minimal example how to run a process + a script so I provide one. I modify the example they provide in their docs.
Say we want to run nginx (or any process that runs until end of container lifetime) plus some shell script myscript.sh.

Local directory structure:

./Dockerfile
./myscript.sh
./s6-overlay/s6-rc.d/myapp/type
./s6-overlay/s6-rc.d/myapp/up
./s6-overlay/s6-rc.d/user/contents.d/myapp

Dockerfile:

FROM ubuntu
ARG S6_OVERLAY_VERSION=3.1.4.1

RUN apt-get update && apt-get install -y nginx xz-utils
RUN echo "daemon off;" >> /etc/nginx/nginx.conf

# Minimal set of dependecies required for s6
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz

# Overhead of files to manage processes via s6
COPY s6-overlay /etc/s6-overlay
# Copy the script we intend to run
COPY myscript.sh /home

# CMD is the main process - nothing special here
CMD ["/usr/sbin/nginx"]
# ENTRYPOINT must be /init for s6 to work
ENTRYPOINT ["/init"]

myscript.sh - make sure to make it executable:

#!/bin/bash
echo "foo" > /home/foo.txt
echo "bar" > /home/bar.txt

s6-overlay/s6-rc.d/myapp/type:

oneshot

"an up file contains a single command line" so as soon our script has >1 line, we have to outsource our script to a separate file. Therefore this is our s6-overlay/s6-rc.d/myapp/up:

/home/myscript.sh

s6-overlay/s6-rc.d/myapp/contents.d/myapp is an empty file.


Now we simply need to docker build (...) and docker run -p 80:80 (...). If you have done everything correctly, you should see a log message s6-rc: info: service myapp successfully started at container startup.
You can then visit localhost:80 and run docker exec CONTAINER bash -c "cat /home/foo.txt" to confirm it works as expected.

Note that utilizing s6-rc.d is the recommended way to do it. There's also a legacy way to accomplish this with less overhead by putting myscript.sh into folder /etc/cont-init.d/.