Giter Site home page Giter Site logo

alexst07 / shell-plus-plus Goto Github PK

View Code? Open in Web Editor NEW
150.0 8.0 10.0 1.8 MB

A friendly and modern functional object oriented language for shell script.

Home Page: https://alexst07.github.io/shell-plus-plus/

License: Apache License 2.0

CMake 2.62% C++ 92.31% Shell 4.80% Dockerfile 0.07% C 0.21%
programming-language scripting-language command-line terminal language shell-script shell shellscript glob-pattern glob

shell-plus-plus's Introduction

Introduction

Shell++ is a programming language that aims bring features from modern languages, as facility to manipulate data structures, object oriented programming, functional programming and others, to shell script. https://alexst07.github.io/shell-plus-plus/

Features

  • easy to manipulate data structure, as in Python
  • lambda functions
  • classes
  • executes commands like in Bash
  • pipe, redirection to or from files, sub shell
  • dynamic typing
  • glob and recursive glob
  • closures
  • reference counter
  • operator overriding

Why another programming language?

I wanted a language that runs shell commands like Bash, and manipulate data structure with the ease of Python, I searched, but I did not find any that met my requirements, so I decided to use my free time to create one.

This is still in development, but it has already made life easier for me in many day-to-day tasks, maybe this might be useful for someone else, so I shared it on github.

However everything here can change without warning, because to date, the only purpose of it is MAKE MY LIFE EASIER AND ONLY THAT.

Examples

Follows some examples just to get an idea of how the language works.

A powerful glob

Shell++ has a powerful glob, that allows you to perform laborious tasks in a few lines. For example, suppose you want to modify the extension of all txt files to csv recursively.

for p, [n] in G"**/*.txt" {
  mv ${p} ${p.parent_path()/"{n}.csv"}
}

List comprehension

files = [f for f in $(ls) if path(f).size("M") > 50]

In this example files will be an array with all files listed by ls command with size bigger than 50Mb.

Try-Catch

try catch helps you handle situations in an elegant way, for example, instead of giving a raw message to the user's face, you can treat the message that a command does not exist.

try {
  git clone [email protected]:alexst07/shell-plus-plus.git
} catch InvalidCmdException as ex {
  print("git not installed [msg: {ex}]")
}

Reading user input

line = read()
print(line)

Function read is used to read user input, until the enter key is pressed, and the next line print the user input.

Reading file line by line

for line in file("example.txt") {
  print("line: {line}")
}

Reading file line by line in shell style

shell {
  while let line = read() {
    print("line: {line}")
  }
} < example.txt

Hello World

echo Hello World
print("Hello World")

In Shell++ you can use an other program as echo, or a native function print.

String operations

Length

str = "example"
print("length: {len(str)}") # length: 7

Upper case and lower case

x = "HELLO"
echo ${x} # HELLO
echo ${x.lower()} # hello
echo ${x.upper()} # HELLO

Substring

str = "string test"
str1 = str[2:] # ring test"
str2 = str[:-2] # string te
str3 = str[2:4] # ri

Replace

filename = "Path of the bash is /bin/bash"
print("After Replacement: {filename.replace('bash', 'sh')}")
# After Replacement: Path of the sh is /bin/sh
print("length: {len(str)}")

Pass variables to command

echo ${t} # Hello
echo ${t + " World"} # Concatenate string
# ${} solve an expression and return the value to echo

Command examples

Pipeline

echo "Test" | cat # simple pipeline
ls src* | grep -e "test" # using glob

# using variables content as command
cip = "ipconfig"
cgrep = ["grep", "-e", "10\..*"]
${cip} | ${cgrep} # pass an array to command

Redirecting to and from file

cat < filename.txt
cat < filename.txt | grep test
cat < filename.txt | grep test > result.txt

# using variables
filename = "filename.txt"
result = "result.txt"
cat < ${filename} | grep test > ${result}

Concatenate file

echo line1 > test.txt
echo lene2 >> test.txt

test.txt has this content: line1 lene2

Redirecting error output

prog_test 2> /dev/null

Any output on stderr go to /dev/null.

Assign command to variable

p = $(prog_test -opt 1)

# check result
if p {
  print(p)
} else {
  print("error: {p.err()}")
}

Check the status result of the command prog_test, if prog_test exited normally with status 0, the result is true and the line print(p) is executed, if not, the else is executed.

Iterating command result

for f in $(ls -a mypath) {
  print("file: {f}")
}

Print all lines generated by ls command.

Execute command from simple variabel

ex = "ls"
${ex} # execute ls commad

Execute command from array variabel

scp = ["sch", "-i", "path/file.pem", "user@addr:path"]
${scp} # execute command from iterator

It is possible execute a command from an array, using this feature you have much more flexibility to mount the command with its arguments using arrays.

Glob and recursive glob

file *.txt

Glob in this case works like in bash.

for f in g"**/*.txt" {
  file ${f}
}

iterate over all txt files recursively

file **/*.txt

This peace of code has the same output from above

Path

Check if a path or file exists

if path("/home/me/file.txt").exists() {
  print ("file exists")
}

Smart path comparison

p1 = path("/home/../home/me/file.txt")
p2 = path("/home/me/file.txt")

print(p1 == p2) # true

p1 = p"/home/me/file.txt"
p2 = p"/home/me/file2.txt"

print(p1 == p2) # false

In Shell++ you can compare relatives path, it means, using path object you compare path not string.

Array

In Shell++ is easy create and access array, it works like in Python

array1 = ['physics', 'chemistry', 1997, 2000];
array2 = [1, 2, 3, 4, 5, 6, 7 ];

print("array1[0]: {array1[0]}")
print("array2[1:5]: {array2[1:5]}")

Output:

array1[0]:  physics
array2[1:5]:  [2, 3, 4, 5]

Map

Map is a hash map object, it works like dictionary in Python.

m = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}

print("m['Name']: {m['Name']}")
print("m['Age']: {m['Age']}")

Output:

m['Name']:  Zara
m['Age']:  7

Functions

In Shell++ function is not a command as in Bash, functions in Shell++ are similar with Python.

func test(a, b = 5) {
  m = {"sum": a+b, "sub":a-b}
  return m
}

print(test(6)["sum"])

Output:

11

Lambda

Return a closure function

func ftest(a) {
  v = ["echo", "ls", a]

  # closures
  return func(x) {
    return v.append(x)
  }
}

The keyword lambda is valid too. This example convert all array elements to upper case string.

vec = ["apple", "orange", "grape"]
vec.map(lambda x: x.to_upper())

Classes

# Complex number
class Complex {
  func __init__(r, i) {
    this.r = r
    this.i = i
  }

  func __add__(n) {
    return Complex(n.r + this.r, n.i + this.i)
  }

  func __sub__(n) {
    return Complex(n.r - this.r, n.i - this.i)
  }

  func __print__() {
    return "{string(this.r)} {string(this.i)}i"
  }
}

c1 = Complex(2, 3)
c2 = Complex(1, 2)
c = c1 + c2

print(c)

Output:

3 + 5i

Quick-sort

func quicksort(A, lo, hi) {
  if lo < hi {
    p = partition(A, lo, hi)
    A = quicksort(A, lo, p - 1)
    A = quicksort(A, p + 1, hi)
  }

  return A
}

func partition(A, lo, hi) {
  pivot = A[hi]
  i = lo
  j = lo

  for j in range(lo, hi) {
    if A[j] <= pivot {
      A[i], A[j] = A[j], A[i]
      i = i + 1
    }
  }

  A[i], A[hi] = A[hi], A[i]
  return i
}

a = [1,4,6,4,10,11,22,100,3,3,9]
print(quicksort(a, 0, len(a) - 1))

Output:

[1, 3, 3, 4, 4, 6, 9, 10, 11, 22, 100]

Check if a command exists

If you need execute a no standard application, sometimes you need to check if the application is installed, with Shell it is a way.

func exist_cmd(name) {
  c = $(which ${name})

  if string(c) == "" {
    return false
  }

  return true
}

print("Does cmd exist?")
print("ls: {exist_cmd('ls')}")
print("non-exist-cmd { exist_cmd('non-exist-cmd')}")

the command which inside $() have as result an cmd object, you can convert its result to string and handle it.

Building

Requirements:

  • A compiler that supports C++ 14 (gcc or clang)
  • Boost
  • Readline
  • CMake
  • Linux

Compiling

Ubuntu

First you need intall the requirements to build Shell++

# apt-get install -y build-essential
# apt-get install -y libboost-all-dev libreadline6 libreadline6-dev git cmake

With the requirements installed, make a clone of the repository

$ git clone [email protected]:alexst07/shell-plus-plus.git

After the clone, change to directory of the project

$ cd shell-plus-plus

To compile the project, you need create a directory when the cmake files will be generated, and so, build the project

$ mkdir build
$ cd build
$ cmake ..
$ make

Now you can already use the Shell++, the binary is generated at shell/ directory. To use Shell++ to execute a script, first, create an example script called my.shpp.

echo my first script in shell++

In the build path, call the binary to execute your script

$ ./shell/shpp my.shpp

You should see the output:

my first script in shell++

If you saw this output your build is working, if you want install Shell++.

$ sudo make install

shell-plus-plus's People

Contributors

alexst07 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shell-plus-plus's Issues

Compilation fails on MSVC Windows

I used Boost-msvc 1.67 and readline 8.0.

Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19042.
The compiler C:/Program Files (x86)/Microsoft Visual Studio/2019/Preview/VC/Tools/MSVC/14.28.29304/bin/Hostx64/x64/cl.exe has no C++14 support. Please use a different C++ compiler.
Configuring done
Generating done
❯ cmake --build ./build
Microsoft (R) Build Engine version 16.8.0-preview-20452-03+5dee11854 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

cl : command line error D8021: invalid numeric argument '/Wextra' [C:\Users\aminy\Documents\GitHub\Shell\shell-plus-plu
s\build\src\shpp.vcxproj]

Should I try with Clang or GCC instead?

whereis example probably will not work on mac

https://github.com/alexst07/shell-plus-plus#check-if-a-command-exists

I did not run on Mac but since the output of whereis is different on Linux and Mac, I assume the example would be broken on Mac:

# Mac
$ whereis ls
/bin/ls
$ whereis no-such-prog
(no output at all)

# Linux
$ whereis ls
ls: /bin/ls /usr/share/man/man1/ls.1.gz
$ whereis no-such-prog
no-such-prog:

I would consider which. It works the same on Mac and Linux and uses exit code to convey whether the command was found.

Binaries

We can set up continuous integration to build shell-plus-plus and provide binaries.

I can help with this!

Glob compiling issues breaking parts of interpreter

When trying to use the sample loop project, I get this error:

File: 'samples/language/9-loop.sh'
line: 81 >> files = [file for file in $(ls) if file.size("k") > 10]
Error: symbol size not found

I tried compiling this on Ubuntu 18.04.

When I was compiling the project, I got this error:

/home/ubuntu/shell-plus-plus/glob-cpp/include/glob-cpp/glob.h: In instantiation of ‘std::tuple<long unsigned int, long unsigned int> glob::StateMatch::Next(glob::String&, size_t) [with charT = char; glob::String = std::__cxx11::basic_string; size_t = long unsigned int]’:
/home/ubuntu/shell-plus-plus/src/utils/glob.cc:92:1: required from here
/home/ubuntu/shell-plus-plus/glob-cpp/include/glob-cpp/glob.h:120:56: warning: unused parameter ‘str’ [-Wunused-parameter]
std::tuple<size_t, size_t> Next(const String& str,
^~~
/home/ubuntu/shell-plus-plus/glob-cpp/include/glob-cpp/glob.h: In member function ‘std::tuple<long unsigned int, long unsigned int> glob::StateGroup::Next(glob::String&, size_t) [with charT = char]’:
/home/ubuntu/shell-plus-plus/glob-cpp/include/glob-cpp/glob.h:518:3: warning: control reaches end of non-void function [-Wreturn-type]
}
^

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.