Skip to content

Commit

Permalink
adds version 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmycanady committed Aug 2, 2018
1 parent 882b935 commit 90a9c3e
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 56 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
builds/linux/psminimize
builds/windows/psminimize
23 changes: 10 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
# PowerShellMinimize
IN PROGRESS
# psminimize
psminimize is a simple utility that tries to minimize a powershell script file. It only uses basic logic to perform the minimization but in general can reduce a ps1 file by half depending on the variable name length. As this is really just a fancy find and replace there are some edge cases to watch out for.

## Limitations
* Function parameter variables will be renamed. If you define the function/cmdlet within the script and rely on it's name when calling later you will need to manually fix the calling statement.

## Usage
`psminimize -s script.ps1 -o script.min.ps`

# Steps?
- [X] remove comments
- [ ] shorten variable names
- find all variables.
- replace all with unique names
- generate short names to replace unique
- replace with new short names
- [ ] shorten function names
- [ ] remove returns
- [ ] alias cmdlets

|long|short|description|required|
|----|----|----|----|
|script-path|s|The path to the script file to minimize.|true|
|output-path|o|The path to write the script two.|true|



4 changes: 4 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

env GOOS=linux GOARCH=amd64 go build -o ./builds/linux/psminimize
env GOOS=windows GOARCH=amd64 go build -o ./builds/windows/psminimize
126 changes: 83 additions & 43 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ import (
"sort"
"strconv"
"strings"
"time"

"github.com/google/uuid"
"github.com/ogier/pflag"
)

const VERSION = "1.0"

const (
CHARComment byte = 35
ESCChar1 byte = 92
Expand All @@ -20,6 +24,12 @@ const (
GT byte = 62
)

var (
cVersion = pflag.BoolP("version", "v", false, "Show version information")
cScriptPath = pflag.StringP("script-path", "s", "", "The path to the PowerShell script file.")
cOutputPath = pflag.StringP("output-path", "o", "", "The path to the output file including name.")
)

var (
varShortNames = []byte{65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121}
)
Expand All @@ -30,6 +40,7 @@ type PSVariable struct {
UniqueName string
ShortName string
Count int
Reserved bool
}

// PSVariables represents a slice of PSVariable structs that can be
Expand All @@ -47,7 +58,6 @@ func (p PSVariables) assignUniqueRandomNames() {
panicOnErr(err)
p[i].UniqueName = fmt.Sprintf("$~~%s", strings.ToUpper(id.String()))
}

sort.Sort(PSVariablesNameMod(p))
}

Expand All @@ -72,6 +82,44 @@ func (p PSVariables) replaceUniqueWithShort(lines []string) {
}
}

// Sort sorts the PSVariable by count.
func (p PSVariables) Sort() {
sort.Sort(p)
}

// generateShortNames generates short names for all variables making sure
// the more used variables have the shortest name.
func (p PSVariables) generateShortNames() {

var count int
var nameIter int
for i := 0; i < len(p); i++ {
if p[i].Reserved {
continue
}
s := "$" + string(varShortNames[nameIter-(51*count)])
if count > 0 {
s = s + strconv.Itoa(count-1)
}

p[i].ShortName = s

if ((nameIter + 1) % 51) == 0 {
count++
}
nameIter++
}
}

// shortenVariables shorts all variables found in lines.
func (p PSVariables) shortenVariables(lines []string) {
p.Sort()
p.assignUniqueRandomNames()
p.generateShortNames()
p.replaceVariablesWithUnique(lines)
p.replaceUniqueWithShort(lines)
}

// PSVariablesNameMod allows sorting based on original name length.
type PSVariablesNameMod PSVariables

Expand All @@ -89,11 +137,28 @@ func panicOnErr(e error) {
}

func main() {
pflag.Parse()

if *cVersion {
fmt.Printf("psminimize version %s\n", VERSION)
return
}

if *cScriptPath == "" {
fmt.Println("no file provided")
return
}
if *cOutputPath == "" {
fmt.Println("no output file provided")
return
}

var minimizedLines = make([]string, 0, 20)
var originalLines = make([]string, 0, 0)
var start = time.Now()

// Reading the file into the original array and duplicate for minimized.
f, err := os.Open("sample.ps1")
f, err := os.Open(*cScriptPath)
panicOnErr(err)
defer f.Close()
scanner := bufio.NewScanner(f)
Expand All @@ -113,9 +178,10 @@ func main() {

//printComparison(originalLines, minimizedLines)

saveToFile(minimizedLines, "./test.ps1")
saveToFile(minimizedLines, *cOutputPath)

fmt.Printf("minimization complete %d -> %d = %f\n", getLength(originalLines), getLength(minimizedLines), (float64(getLength(minimizedLines)) / float64(getLength(originalLines)) * 100))
// fmt.Printf("minimization completed in %d seconds and reduced by %f %d -> %d = %f\n", getLength(originalLines), getLength(minimizedLines), (float64(getLength(minimizedLines)) / float64(getLength(originalLines)) * 100))
fmt.Printf("minimization completed in %f seconds and reduced by %f%%\n", time.Since(start).Seconds(), (100 - (float64(getLength(minimizedLines)) / float64(getLength(originalLines)) * 100)))
}

func getLength(lines []string) int {
Expand Down Expand Up @@ -243,56 +309,27 @@ func getVariables(lines []string) PSVariables {
}
}
for k, v := range psVarMap {
// Skipping any reserved.
p := PSVariable{OriginalName: k, Count: v}
// Adding any reserved.
_, ok := reservedPSVariables[k]
if ok {
continue
p.Reserved = true
p.ShortName = p.OriginalName
}

// Skipping any starting with an escape character.
// Making sure we don't replace any escaped sequencing by marking as
// reserved.
if string(k[0]) == "`" {
continue
p.Reserved = true
p.ShortName = p.OriginalName
}

psVars = append(psVars, PSVariable{OriginalName: k, Count: v})
psVars = append(psVars, p)
}

return psVars
}

// Sort sorts the PSVariable by count.
func (p PSVariables) Sort() {
sort.Sort(p)
}

// generateShortNames generates short names for all variables making sure
// the more used variables have the shortest name.
func (p PSVariables) generateShortNames() {

var count int
for i := 0; i < len(p); i++ {
s := "$" + string(varShortNames[i-(51*count)])
if count > 0 {
s = s + strconv.Itoa(count-1)
}

p[i].ShortName = s

if ((i + 1) % 51) == 0 {
count++
}
}
}

// shortenVariables shorts all variables found in lines.
func (p PSVariables) shortenVariables(lines []string) {
p.Sort()
p.assignUniqueRandomNames()
p.replaceVariablesWithUnique(lines)
p.generateShortNames()
p.replaceUniqueWithShort(lines)
}

// getNextShortname returns the next shortname to use. Use 0 for the first call.
func getNextShortName(lastName byte) byte {
if lastName == 0 {
Expand All @@ -315,6 +352,8 @@ func getNextShortName(lastName byte) byte {

}

// removeExtraSpaces removes any extra spaces around various powershell
// operators.
func removeExtraSpaces(lines []string) {
for i := range lines {
// fmt.Println(lines[i])
Expand Down Expand Up @@ -362,6 +401,7 @@ func removeExtraSpaces(lines []string) {
}
}

// removeAllNewLines removes all new lines that adding semicolons as needed.
func removeAllNewLines(lines []string) []string {
minimizedLines := make([]string, 0, len(lines))

Expand All @@ -377,7 +417,7 @@ func removeAllNewLines(lines []string) []string {

switch l[len(l)-1:] {
// switch lines[i][len(lines[i])-1:] {
case "}", "{", "(":
case "}", "{", "(", ";":

case "]":
l = l + "\n"
Expand Down

0 comments on commit 90a9c3e

Please sign in to comment.