Exporting Project Files into a Markdown Document with Bash
Sometimes you want to collect the contents of several source files into one Markdown file.
This can be useful when you want to:
- share a small project with someone
- send code to an AI assistant
- create a quick snapshot of important files
- review multiple files in one place
The following Bash command scans the current directory recursively, finds files with specific extensions, skips unwanted folders like node_modules, and writes everything into a file called project.md.
bashfind . \ \( -type d \( -name "node_modules" -o -name ".git" -o -name "dist" \) -prune \) -o \ -type f \( -name "*.js" -o -name "*.ts" -o -name "*.json" \) \ -not -name "project.md" \ -print0 | while IFS= read -r -d '' file; do rel="${file#./}" printf '/%s\n\n```\n' "$rel" cat "$file" printf '\n```\n\n' done > project.md
What the command does
The command starts from the current directory, represented by ..
bashfind .
This tells find to search inside the folder where the command is run, including all subfolders.
Excluding folders
This part skips folders that should not be scanned:
bash\( -type d \( -name "node_modules" -o -name ".git" -o -name "dist" \) -prune \) -o
Here, we exclude:
textnode_modules .git dist
That is important because folders like node_modules can contain thousands of files. Including them would make the output huge and usually not useful.
You can add more folders to exclude by extending this section:
bash-name "node_modules" -o -name ".git" -o -name "dist" -o -name "build"
Choosing file types
This part decides which files should be included:
bash-type f \( -name "*.js" -o -name "*.ts" -o -name "*.json" \)
It means:
textinclude regular files ending in .js, .ts, or .json
You can change this depending on your project.
For example, for Python files:
bash-name "*.py"
For JavaScript and CSS files:
bash-name "*.js" -o -name "*.css"
For a React project, you might use:
bash-name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx"
Avoiding project.md itself
This part prevents the script from reading the output file while it is being created:
bash-not -name "project.md"
Without this, there is a chance the command could include project.md inside itself, especially if the file already exists.
Handling filenames safely
The command uses:
bash-print0
and:
bashwhile IFS= read -r -d '' file; do
This makes the script safer when filenames contain spaces, quotes, or unusual characters.
For example, a file named:
textmy component.js
will still be handled correctly.
Formatting each file in Markdown
Inside the loop, this line removes the leading ./ from the file path:
bashrel="${file#./}"
So instead of this:
text./src/index.js
the output becomes:
text/src/index.js
Then the script writes the file path into the Markdown file:
bashprintf '/%s\n\n```\n' "$rel"
After that, it writes the actual file content:
bashcat "$file"
Finally, it closes the Markdown code block:
bashprintf '\n```\n\n'
The final result looks like this:
markdown/src/index.js ``` console.log("Hello world"); ``` /package.json ``` { "name": "my-project" } ```
Saving everything to project.md
At the end, this part writes all output into a file:
bashdone > project.md
So after running the command, you will get a new file named:
textproject.md
That file will contain the paths and contents of all matched files.
Why this is useful
This is a quick way to turn a project into a readable Markdown snapshot.
It is especially useful when you want to share code without uploading an entire folder, or when you want to provide context to a tool that accepts Markdown input.
You can customize two main things:
The folders to skip:
bash-name "node_modules" -o -name ".git" -o -name "dist"
And the file extensions to include:
bash-name "*.js" -o -name "*.ts" -o -name "*.json"
With small changes, this command can work for almost any kind of project.