Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,21 @@
### Example Usage
``` sh
# Create a standard script example
$ python ./py2shell.py --output basic.sh --datasource templates/basic.json
$ ./py2shell.py --output basic.sh --datasource basic
```

``` sh
# Create a standard script example and make it executeable
$ python ./py2shell.py --output basic.sh --datasource templates/basic.json --exec
$ ./py2shell.py --output basic.sh --datasource basic --exec
```

### Advanced Examples
``` sh
# Create an advanced script example and make it executeable
$ ./py2shell.py --output basic.sh --datasource shift::shifting_args --exec
```


### Test your scripts
1. All script you create are saved into a locally generated directory named **scripts**.
2. The previously created script is executed using the following commands.
Expand Down
112 changes: 91 additions & 21 deletions auto_shell_scripting/TemplateBuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import string
import time


@dataclass
class TemplateBuilder:
value: str
Expand Down Expand Up @@ -40,14 +41,33 @@ def buildCaseStatement(self, case_statement: dict) -> str:
if key == "switch":
for val in value:
for parameter, statement in val.items():
switch_array += "\t{}) {};;\n".format(parameter, statement)
switch_array += "\t{}) {};;\n".format(
parameter, statement)
statement_builder += "case ${} in\n".format(goal)
statement_builder += switch_array
statement_builder += "esac\n"
return statement_builder

def buildNestedCaseStatement(self, case_statement: dict) -> str:
print("Building, case statement")
switch_array = ""
statement_builder = ""
goal = ""
for key, value in case_statement:
if key == "goal":
goal = value
if key == "switch":
for val in value:
for parameter, statement in val.items():
switch_array += "\t\t{}) {};;\n".format(
parameter, statement)
statement_builder += "\tcase ${} in\n".format(goal)
statement_builder += switch_array
statement_builder += "\tesac\n"
return statement_builder

def buildForStatement(self, loop_statement: dict) -> str:
print("Building, loop statement")
print("Building, loop statement")
statement_builder = ""
for_statements = ""
goal = ""
Expand All @@ -73,28 +93,35 @@ def buildWhileStatement(self, loop_statement: dict) -> str:
goal = value
if key == "run":
for k in value:
while_statements += "\t{}".format(self.iterateRun(k))
if k.keys().__contains__('conditions'):
condition = [_v for _k, _v in k.items()][0]
while_statements += "{}".format(
self.getConditionBuilder(condition))
else:
while_statements += "\t{}".format(self.iterateRun(k))
if while_statements:
statement_builder += "while [ {} ]; do\n".format(goal)
statement_builder += while_statements
statement_builder += "done\n"
return statement_builder

def getConditionBuilder(self, value) -> str:
template = ""
for arr in value:
# Get Condition Type
condition_type = [ v for k,v in arr.items() ][0]
condition_type = [v for k, v in arr.items()][0]
if condition_type == "if":
template += self.buildIfStatement(arr.items())
elif condition_type == "case":
template += self.buildCaseStatement(arr.items())
elif condition_type == "case.nested":
template += self.buildNestedCaseStatement(arr.items())
elif condition_type == "for":
template += self.buildForStatement(arr.items())
elif condition_type == "while":
template += self.buildWhileStatement(arr.items())
return template

def buildFunction(self, function: dict) -> str:
print("Building, function statement")
statement_builder = ""
Expand All @@ -103,19 +130,44 @@ def buildFunction(self, function: dict) -> str:
for key, value in function:
if key == "name":
name = value
if key == "statements":
for stmt in value:
if key == "statements":
for stmt in value:
if stmt.keys().__contains__('conditions'):
condition = [ v for k,v in stmt.items() ][0]
condition = [v for k, v in stmt.items()][0]
statements += self.getConditionBuilder(condition)
else:
statements += self.iterateRun(stmt)
statements += self.iterateRun(stmt)
if key == "control" or key == "onliner":
statements += self.buildOnliner(value)
if statements:
statement_builder += "{}(){}".format(name,'{')
statement_builder += "\t" + statements + "\n"
statement_builder += "{}(){}".format(name, '{')
statement_builder += statements + "\n"
statement_builder += "}\n"
return statement_builder

def replaceMetaTag(self, variable: str) -> str:
variable = variable.replace('__SPACE__', ' ')
variable = variable.replace('__BEGIN__', '\"')
variable = variable.replace('__NEWLINE__', '\\n')
variable = variable.replace('__END__', '\"')
return variable

def buildControl(self, value: dict) -> str:
line = ""
key = list(value.keys())
values = list(value.values())
for v in values[0]:
line += self.replaceMetaTag(v)
return "\n{} {}".format(key[0], line)

def buildOnliner(self, value: dict) -> str:
line = ""
key = list(value.keys())
values = list(value.values())
for v in values[0]:
line += self.replaceMetaTag(v)
return "\n{} {}".format(key[0], line)

def iterateRun(self, template_data: dict) -> str:
line_statement = ""
run_type = ""
Expand All @@ -130,33 +182,51 @@ def iterateRun(self, template_data: dict) -> str:
elif run_type == "command_call" and key != "type":
line_statement += "{} {}\n".format(key, value)
except AttributeError as ae:
print("Check datasource syntax and ensure you are using the correct datatype (array, object)")
print(
"Check datasource syntax and ensure you are using the correct datatype (array, object)")
print(ae)
exit(1)
return line_statement

def getTemplateData(self, datasource: str) -> dict:
template_data = {}
with open(datasource, "r") as f:
template_data = json.load(f)
try:
# Remove extension if present
datasource = datasource.replace('::', '/')
datasource = "templates/{}.json".format(datasource)
if os.path.exists(datasource):
with open(datasource, "r") as f:
template_data = json.load(f)
else:
print("\033[35mWarning: \033[33mMissing or unable to find template (name: {})\033[0m".format(
datasource))
except Exception as e:
print(
"Error: Missing or unable to find template (name: {})".format(datasource))
return template_data

def generate_template(self, template_data: dict) -> str:
template = ""
for key, value in template_data.items():
if key in ["purpose"]:
print("\033[36mPURPOSE: \033[33m{}\033[0m".format(value))
# Writes the initial shell
if key in [ "shell.type" ]:
if key in ["shell.type"]:
print("Shell Type is {}".format(value))
template += "#!/usr/bin/env {}\n\n".format(value)
# template += "set -x\n\n"
elif key in [ "define.variables" ]:
elif key in ["define.variables"]:
# Creates a lot of variables
for k,v in value.items():
template += "{}={}\n".format(k,v)
elif key in [ "conditions" ]:
for k, v in value.items():
template += "{}={}\n".format(k, v)
elif key in ["conditions"]:
# Iterate
template += self.getConditionBuilder(value)
elif key in [ "functions" ]:
elif key in ["functions"]:
for arr in value:
template += self.buildFunction(arr.items())
elif key in ["control"] or key in ["command_call"]:
template += self.buildControl(value)
elif key in ["onliner"]:
template += self.buildOnliner(value)
return template
37 changes: 37 additions & 0 deletions auto_shell_scripting/utils/TemplateLister.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3.10

from dataclasses import dataclass
import json
import os
import string
import time


@dataclass
class TemplateLister:

def getTemplates(self, path) -> str:
results = []
purpose = " - "
for entry in os.listdir(path):
full_path = os.path.join(path, entry)
if os.path.isdir(full_path):
results.extend(self.getTemplates(full_path))
else:
strOutput = open(full_path,"r")
obj = json.loads(strOutput.read())
try:
purpose += "\033[32m{}".format(obj['purpose'])
except Exception as e:
purpose = " - \033[31m{}".format('missing purpose')
pass
results.append(entry.replace('.json', purpose))
return results

def createMenu(self):
counter = 0
templates = sorted(self.getTemplates("templates"))
print("\033[36m==== Available Templates: ====\033[0m")
for template in templates:
counter += 1
print("\033[33m{}: \033[35m{}\033[0m".format(counter, template))
43 changes: 43 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Changelog

All notable changes to this project will be documented in this file.

<!--
## [Unreleased]

## [X.X.X] - YYYY-MM-DD

### Added
-
### Fixed
-
### Changed
-
### Removed
-
-->

## [1.0.1] - 2023-04-12

### Added

- Adding setup shellscript
- More templates
- More template logic determinators

### Changed

- New Project logo
- Fix the standard template
- Reformatting all scripts
- Reformatting all templates

## [1.0.0] - 2023-01-06

### Added

- Initial Commit

<!-- [unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.1...HEAD -->
[1.1.0]: https://github.com/denezt/automate-shell-scripting/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/denezt/automate-shell-scripting/releases/tag/v1.0.0
Binary file modified img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 51 additions & 27 deletions py2shell.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,62 @@
#!/usr/bin/env python

import os
import argparse
from auto_shell_scripting import TemplateBuilder as TemplateBuilder
from auto_shell_scripting.utils import TemplateLister as TemplateLister

# Set Program Parameters
parser = argparse.ArgumentParser(prog='py2shell', description='Tool for automating shell scripts creation')
parser.add_argument('--make-executable', action='store_true', default=False, help='Make script executable')
parser.add_argument('--exec', action='store_true', default=False, help='Make script executable')
parser.add_argument('--datasource', type=str, required=True, help='Path to JSON data source')
parser.add_argument('--output', type=str, required=True, help='Name of target output script name')
parser = argparse.ArgumentParser(
prog='py2shell', description='Tool for automating shell scripts creation')
parser.add_argument('--make-executable', action='store_true',
default=False, help='Make script executable')
parser.add_argument('--exec', action='store_true',
default=False, help='Make script executable')
parser.add_argument('--datasource', type=str, help='Path to JSON data source')
parser.add_argument('--output', type=str,
help='Name of target output script name')
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
parser.add_argument("--templates", action="store_true",
help="Show available templates")

args = parser.parse_args()
print(args)

# Read the JSON file containing the template data
datasource = args.datasource
output = args.output
view_templates = args.templates

if args.debug:
print(args)

if '__main__' == __name__:
if output.endswith('.sh') and datasource.endswith('.json'):
templateBuilder = TemplateBuilder.TemplateBuilder(value="", collector="")
template_data = templateBuilder.getTemplateData(datasource=datasource)
# Generate the template
template = templateBuilder.generate_template(template_data)
print(template)
# Write the template to a file
if output.endswith('.sh'):
if not os.path.exists("scripts"):
os.mkdir("scripts")
with open("scripts/{}".format(output), "w") as f:
f.write(template)
if args.make_executable or args.exec:
script_name = "scripts/{}".format(output)
if os.path.isfile(script_name):
print("Making, script executeable")
try:
os.chmod("{}".format(script_name),777)
except FileNotFoundError as fnfe:
print(fnfe)
print("Template generated successfully!")
if output is not None and datasource is not None:
try:
if output.endswith('.sh'):
templateBuilder = TemplateBuilder.TemplateBuilder(
value="", collector="")
template_data = templateBuilder.getTemplateData(
datasource=datasource)
# Generate the template
template = templateBuilder.generate_template(template_data)
print(template)
# Write the template to a file
if output.endswith('.sh'):
if not os.path.exists("scripts"):
os.mkdir("scripts")
with open("scripts/{}".format(output), "w") as f:
f.write(template)
if args.make_executable or args.exec:
script_name = "scripts/{}".format(output)
if os.path.isfile(script_name):
print("Making, script executeable")
try:
os.chmod("{}".format(script_name), 777)
except FileNotFoundError as fnfe:
print(fnfe)
print("Template generated successfully!")
except AttributeError as ae:
pass
if view_templates:
t = TemplateLister.TemplateLister()
t.createMenu()
Loading