Typescript ESM module to build Bourne Shell scripts comfortably, dynamically
npm install shell-factory``sh`
npm install shell-factory
To give you a jump start using regularely used commands, please refer to the common commands collection (shell-factory-cocos).
`typescript
const script = new Script([
'echo "Hello World"',
new Command('echo "Hello Command"').setComment('Command class example'),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
echo "Hello World"
echo "Hello Command" # Command class example
`
`typescript
const script = new Script([
'read -p "What do you want to say? " input',
new If('"$input" == "Hello"', [
'echo "Hello"',
]).elseIf('"$input" == "Bye"', [
'echo "Bye"',
]).else([
'echo "What shall we do with the drunken sailor?"',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
read -p "What do you want to say? " input
if [ "$input" == "Hello" ]; then
echo "Hello"
elif [ "$input" == "Bye" ]; then
echo "Bye"
else
echo "What shall we do with the drunken sailor?"
fi
`
`typescript
const script = new Script([
'input=0',
new While(true, [
'input=$(expr $input + 1)',
'echo $input',
'sleep 1',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
input=0
while [ 1 ]; do
input=$(expr $input + 1)
echo $input
sleep 1
done
`
`typescript
const script = new Script([
'input=0',
new Until(false, [
'input=$(expr $input + 1)',
'echo $input',
'sleep 1',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
input=0
until [ 0 ]; do
input=$(expr $input + 1)
echo $input
sleep 1
done
`
`typescript
const script = new Script([
new For('i', [true, 2, 'three'], [
'echo $i',
'sleep 1',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
for i in true 2 three; do
echo $i
sleep 1
done
`
`typescript
const script = new Script([
new Select('selection', ['a', 'b', 'c'], [
'echo "You\'ve selected $selection"',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
select selection in a b c; do
echo "You've selected $selection"
done
`
`typescript
const script = new Script([
'read -p "Where are we running? " input',
new Case('$input', [
new CaseOption('We need some time to clear our heads', [
'echo "Keep on working \'til we\'re dead"',
]),
new CaseOption('*', [
'echo "I have no idea"'
]),
'echo "I\'m added to the last CaseOption"',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
read -p "Where are we running? " input
case $input in
"We need some time to clear our heads")
echo "Keep on working 'til we're dead"
;;
*)
echo "I have no idea"
echo "I'm added to the last CaseOption"
;;
esac
`
`typescript
const script = new Script([
new Function('hello_world', [
'echo "Greetings $first_name, $last_name"',
], [
'first_name',
'last_name',
]),
'hello_world',
]).dump();
console.log(script);
`
`sh
#!/bin/sh
hello_world() {
local first_name="$1"
local last_name="$2"
echo "Greetings $first_name, $last_name"
}
hello_world
`
The Function class additionally provides the call-method to return a function-call Command with the provided parameters.
`typescript
const exitFunc = new Function('exit_function', [
new If('"$2" != ""', [
'echo "$2"',
]),
new Command('exit $1').setComment('Exiting with the provided error-code.'),
]);
const script = new Script([
exitFunc,
new If('-e "hello.txt"', [
exitFunc.call(0),
]).else([
exitFunc.call(-1, 'File doesn\'t exit.'),
])
]).dump();
console.log(script);
`
`sh
#!/bin/sh
exit_function() {
if [ "$2" != "" ]; then
echo "$2"
fi
exit $1 # Exiting with the provided error-code.
}
if [ -e "hello.txt" ]; then
exit_function 0
else
exit_function -1 "File doesn't exit."
fi
`
`typescript
const stringVariable = new StringVariable('string');
const numberVariable = new NumberVariable('number');
const script = new Script([
stringVariable.set(), / Initialize string variable. /
numberVariable.set(0), / Initialze the number variable. /
/ Loop while number variable is less than 5. /
new While(numberVariable.isLess(5), [
/ If string variable is empty, set it to 'Hello'. /
new If(stringVariable.isEqual(), [
stringVariable.set('Hello'),
/ If number variable is 1, append ' again' to string variable. /
]).elseIf(numberVariable.isEqual(1), [
stringVariable.set(stringVariable.append(' again')),
/ Ever other time, append ' and again' to the string variable. /
]).else([
stringVariable.set(stringVariable.append(' and again')),
]),
/ Print the string variable to console. /
echo "${stringVariable.value}",
/ Increment the number variable by 1. /
numberVariable.set(numberVariable.increment()),
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
string=""
number=0
while [ ${number} -lt 5 ]; do
if [ "${string}" = "" ]; then
string="Hello"
elif [ ${number} -eq 1 ]; then
string="${string} again"
else
string="${string} and again"
fi
echo "${string}"
number=$(expr ${number} + 1)
done
`
`typescript
const script = new Script([
new Condition('1 -eq 1')
.and('2 -eq 2')
.or('2 -eq 2'),
new Condition('3 -ne 2')
.setTest(false)
.setComment('Interpreter will throw an error here because the statement doesn\'t make sense.'),
]).dump({
common: { newlinesBefore: 1 }
});
console.log(script);
`
`sh
#!/bin/sh
[ 1 -eq 1 -a 2 -eq 2 -o 2 -eq 2 ]
3 -ne 2 # Interpreter will throw an error here because the statement doesn't make sense.
`
`typescript
const script = new Script([
new While('read -r line', [
'echo "$line"',
]).read('input.txt'),
new Command('cat').read('test.txt'),
]).dump({
detailed: { while: { newlinesAfter: 1 } }
});
console.log(script);
`
`sh
#!/bin/sh
while read -r line; do
echo "$line"
done < input.txt
cat < test.txt
`
`typescript
const script = new Script([
new Command('echo "File content"').write('test.txt'),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
echo "File content" > test.txt
`
`typescript
const file = 'test.txt';
const script = new Script([
new Command('echo "File content"').write(file),
new Command('echo "Additional content"').append(file),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
echo "File content" > test.txt
echo "Additional content" >> test.txt
`
`typescript
const script = new Script([
new Command('cat test.txt')
.pipe('grep -e "hello"')
.pipe('cut -d" " -f0')
.write('test2.txt')
.setComment('Nice chain!'),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
cat test.txt | grep -e "hello" | cut -d" " -f0 > test2.txt # Nice chain!
`
`typescript
const script = new Script([
new Command('echo "File content"')
.pipe('grep -o -e "content"')
.and('echo "ok"'),
new If('1 -eq 1').and('2 -eq 2').addContent([
'echo "What a useless comparison"',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
echo "File content" | grep -o -e "content" && echo "ok"
if [ 1 -eq 1 -a 2 -eq 2 ]; then
echo "What a useless comparison"
fi
`
`typescript
const script = new Script([
new Command('echo "File content"')
.pipe('grep -o -e "content"')
.or('echo "nevermind"'),
new If('1 -eq 1').or('2 -eq 2').addContent([
'echo "What a useless comparison"',
]),
]).dump();
console.log(script);
`
`sh
#!/bin/sh
echo "File content" | grep -o -e "content" || echo "nevermind"
if [ 1 -eq 1 -o 2 -eq 2 ]; then
echo "What a useless comparison"
fi
`
`typescript
const variable = new StringVariable('response');
const script = new Script([
'read -p "What\'s your name? " name',
variable.set(
new If('"$name" != ""', [
'echo "Hello $name"',
]).eval(),
),
new If(variable.isEmpty, [
variable.set(Subshell.eval('echo "I don\'t understand"')),
]),
echo "${variable.value}",
]).dump();
console.log(script);
`
`sh
#!/bin/sh
read -p "What's your name? " name
response=$(
if [ "$name" != "" ]; then
echo "Hello $name"
fi
)
if [ -z "${response}" ]; then
response="$(echo "I don't understand")"
fi
echo "${response}"
`
`typescript
const spacyConfig = {
detailed: {
for: {
newlinesAfter: 2,
},
command: {
newlinesBefore: 1,
indentBeforeComment: 6,
},
}
};
const script = new Script([
new For('i', [1, 2, 3], [
new Command('echo "Iteration $i"').setComment('Far away comment.'),
]),
'echo "First statement"',
new Command('echo "Second statement"').setComment('Another far away comment.'),
'echo "Third statement"',
]).dump(spacyConfig);
console.log(script);
`
`sh
#!/bin/sh
for i in 1 2 3; do
echo "Iteration $i" # Far away comment.
done
echo "First statement"
echo "Second statement" # Another far away comment.
echo "Third statement"
`
`typescript
const meta = new MetaData(); / MetaData container. /
const script = new Script([
new If(1, [
'echo "This is the first statement"',
]).meta(meta),
]);
console.log(script.dump()); / Dump the original script. /
/ Find the If-block in the Script-block by its ID. /
const ifBlock = script.findContent(meta.id)[0];
/ Add another statement to the If-block. /
ifBlock.addContent('echo "Here\'s another statement"');
console.log(script.dump()); / Dump the updated script. /
`
`sh
#!/bin/sh
if [ 1 ]; then
echo "This is the first statement"
fi
`
`sh
#!/bin/sh
if [ 1 ]; then
echo "This is the first statement"
echo "Here's another statement"
fi
`
`typescript
const script = new Script([
new Command().setComment('First line of this script'),
'echo "Is this going to be removed?"',
'echo "Will this also be removed?"',
new Command().setComment('Last line of this script'),
]);
/ Dump the original script. /
console.log(script.dump());
/ Remove statements by pattern. /
script.removeContent(/remove/);
/ Dump the altered script. /
console.log(script.dump());
`
`sh
#!/bin/sh
`sh
#!/bin/shFirst line of this script
Last line of this script
`$3
Blocks and Statements can be altered by retrieving them via their ID or a statement pattern through their parent block (e.g. Script) with the findContent method and altering the returned object(s).`typescript
const meta = new MetaData(); / MetaData container. /
const script = new Script([
new Command('echo "Hello"')
.setComment('This might be altered at the next dump')
.meta(meta), / Get the Statement's meta-data. /
]);
console.log(script.dump()); / Dump the original script. // Find the statement in the script by its ID. /
const statement = script.findContent(meta.id)[0];
/ Update the Statement's value and comment. /
statement.statement = 'echo "World"';
statement.setComment('It has been altered"');
console.log(script.dump()); / Dump the altered script. /
``sh
#!/bin/shecho "Hello" # This might be altered at the next dump
``sh
#!/bin/shecho "World" # It has been altered"
``