Это простое приложение NodeJS, которое принимает исходную папку в качестве входных данных и генерирует сценарий Bash. Сценарий Bash имеет все содержимое файлов и структуру папок в исходной папке и может воссоздавать их при выполнении.

Исходный код доступен здесь: https://github.com/alexadam/folders-to-script

Первый шаг, переберите все файлы в исходной папке:

const fs = require("fs")
const path = require("path")
const listFiles = (dirPath, result) => {
 files = fs.readdirSync(dirPath)
 result = result || ['#!/bin/sh']
 for (const file of files) {
   ///...
 }
 return result
}
const allFiles = listFiles(rootPath)
fs.writeFileSync('./result.sh', allFiles.join('\n'))

Если файл является каталогом, добавьте команду mkdir -p:

for (const file of files) {
 if (fs.statSync(dirPath + "/" + file).isDirectory()) {
 result.push(`mkdir -p ${path.join(dirPath, "/",  file).replace(rootPath, '.')}`)
 result = listFiles(dirPath + "/" + file, result)
 } 
}

В противном случае проверьте, является ли файл двоичным или текстовым, в зависимости от его расширения:

const textExt = ['txt', 'md', 'html', 'json', 'js', 'jsx', 'ts', 'tsx'];
const binaryExt = ['jpg', 'png', 'gif', 'pdf', 'mp3', 'mp4'];
const getFileExt = (filePath) => filePath.split('.').pop()
...
    else {
      const filePath = path.join(dirPath, "/", file);
      const fileExt = getFileExt(filePath);
      const fileContent = fs.readFileSync(filePath);
      if (textExt.includes(fileExt)) {
        result.push(`
cat > ${path.join(dirPath, "/", file).replace(rootPath, '.')} << "EOF"
${fileContent}
EOF
`)
      } else if (binaryExt.includes(fileExt)) {
        const bindata = fileContent.toString('binary');
        const hexdata = new Buffer(bindata, 'ascii').toString('hex');
        result.push(`echo '${hexdata}' | xxd -r -p > ${path.join(dirPath, "/", file).replace(rootPath, '.')}`)
      }
    }
...

Двоичные файлы хранятся в виде шестнадцатеричных строк.

Вы можете добавлять/удалять расширения файлов в массивах textExt или binaryExt.

Это полный скрипт NodeJS:

const fs = require("fs")
const path = require("path")
const rootPath = process.argv[2]
const textExt = ['txt', 'md', 'html', 'json', 'js', 'jsx', 'ts', 'tsx'];
const binaryExt = ['jpg', 'png', 'gif', 'pdf', 'mp3', 'mp4'];
const getFileExt = (filePath) => filePath.split('.').pop()
const listFiles = (dirPath, result) => {
  files = fs.readdirSync(dirPath)
  result = result || ['#!/bin/sh']
  for (const file of files) {
    if (fs.statSync(dirPath + "/" + file).isDirectory()) {
      result.push(`mkdir -p ${path.join(dirPath, "/", file).replace(rootPath, '.')}`)
      result = listFiles(dirPath + "/" + file, result)
    } else {
      const filePath = path.join(dirPath, "/", file);
      const fileExt = getFileExt(filePath);
      const fileContent = fs.readFileSync(filePath);
      if (textExt.includes(fileExt)) {
        result.push(`
cat > ${path.join(dirPath, "/", file).replace(rootPath, '.')} << "EOF"
${fileContent}
EOF
`)
      } else if (binaryExt.includes(fileExt)) {
        const bindata = fileContent.toString('binary');
        const hexdata = new Buffer(bindata, 'ascii').toString('hex');
        result.push(`echo '${hexdata}' | xxd -r -p > ${path.join(dirPath, "/", file).replace(rootPath, '.')}`)
      }
    }
  }
  return result
}
const allFiles = listFiles(rootPath)
fs.writeFileSync('./result.sh', allFiles.join('\n'))

Протестируйте его с помощью node index.js test -› он создаст файл сценария Bash с именем result.sh

Затем в пустой директории выполните скрипт с sh result.sh.