Learned about vertical tabs todayβ¦
We all love ls but what if you want a tree like view, just type:
ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'
Enjoy your output.
Nice use of the cascading sed substitutions!
Maze Game
I created a timed maze game that reads a player defined map using arbitrary text. Has hit detection and a timed win condition.
Add an X for the player, an E for the exit, spaces are where the player can travel and anything else blocks movement.
Iβve been working with arrays so much the idea popped into my head so I put it into code. You can copy/paste in any text based map you find on the Internet (so long as traversable areas are spaces) and the script will make it work. Just add an X for the player and E for exit.
#!/usr/bin/env bash
#
# === USER DEFINED MAP ===
#
# Map key:
# - X for the player
# - E for exit
# - Space for traversable
# - Any other character blocks the player's movement
{ MAP_DATA=$(</dev/stdin); } <<\EOF
Movement: AWSD or HJKL
βββββββββββββββββββββββββ/E\β
β β β β
β β β β β β β β
β β β β β β β β
β β β βββββ β β β
β β β β β
β β βββββββββ βββββ β
β β β β β β
β βββββ β β β βββββ
β β β β
βββββ βββββββββββββ β β
β β β β β β
β β β β β βββββ β
β X β β β
β ^ βββββββββββββββββββββββββ
EOF
# === DEFINE FUNCTIONS ===
MOVE_PLAYER(){
# Define where the player is attempting to move
ATTEMPT_X=$(( $1 + $PLAYER_X ))
ATTEMPT_Y=$(( $2 + $PLAYER_Y ))
ATTEMPT_MAP_Y_LINE=${MAP_Y[$ATTEMPT_Y]}
# Prevent out of bounds travel
if [ "$ATTEMPT_X" = "-1" -o "$ATTEMPT_Y" = "-1" ]; then return 1; fi
# Is the player moving onto a win condition?
if [ "${ATTEMPT_MAP_Y_LINE:$ATTEMPT_X:1}" = "E" ]; then
# Get the finish time, print victory statement and exit
FINISH_TIME=$(( SECONDS - GAME_START ))
printf "You escaped in %s seconds" $FINISH_TIME
if [ $PLAYER_Y == 0 ]; then printf " using \e[0;91mrule 0\e[0m"; fi
printf "!\n\n"
exit 1
fi
# Is the player attempting to move into an empty space?
if [ "${ATTEMPT_MAP_Y_LINE:$ATTEMPT_X:1}" = $EMPTY_BLOCK ]; then
# Erase current player location
PLAYER_MAP_Y_LINE=${MAP_Y[$PLAYER_Y]}
MAP_Y[$PLAYER_Y]="${PLAYER_MAP_Y_LINE:0:$PLAYER_X}${EMPTY_BLOCK}${PLAYER_MAP_Y_LINE:$(($PLAYER_X+1))}"
# Move player coordinates
PLAYER_X=$ATTEMPT_X
PLAYER_Y=$ATTEMPT_Y
# Draw new player location on updated and/or different PLAYER_MAP_Y_LINE
PLAYER_MAP_Y_LINE=${MAP_Y[$PLAYER_Y]}
MAP_Y[$PLAYER_Y]="${PLAYER_MAP_Y_LINE:0:$PLAYER_X}X${PLAYER_MAP_Y_LINE:$(($PLAYER_X+1))}"
# Print map
PRINT_MAP
fi
}
PRINT_MAP(){
clear
#printf '\e[0;92m' # Make green
printf '%s\n' "${MAP_Y[@]}"
#printf '\e[0;0m' # Reset color
}
USER_INPUT_LOOP(){
# Wait for the user to press a key and define it as $KEY
read -s -n 1 KEY
# Move the player according to the KEY's associated movement value in KEY_TO_MOVEMENT
MOVE_PLAYER ${KEY_TO_MOVEMENT[$KEY]}
USER_INPUT_LOOP
}
# === STARTUP ===
# Declare human input keys with movement modifiers
declare -A KEY_TO_MOVEMENT=( [w]="0 -1" [d]="1 0" [a]="-1 0" [s]="0 1" [k]="0 -1" [l]="1 0" [h]="-1 0" [j]="0 1" )
EMPTY_BLOCK="." # Special ANSI invisible character
IFS_OLD="$IFS";
IFS=""
# Replace all spaces with the EMPTY_BLOCK to simplify sequencing and add map to MAP_Y array.
MAP_DATA=$(echo $MAP_DATA | sed "s/ /$EMPTY_BLOCK/g")
readarray -t MAP_Y <<< "$MAP_DATA"
# Get player location by counting prior new lines and prior characters on the player's MAP_Y line.
PLAYER_Y=$(( `echo ${MAP_DATA%X*} | wc -l` - 1 ))
PLAYER_X=$(( `echo ${MAP_Y[$PLAYER_Y]%X*} | wc -m` -1 ))
IFS="$IFS_OLD"
GAME_START=$SECONDS
PRINT_MAP
USER_INPUT_LOOP
ncat: Hosting a public website with a single command
sudo dnf install nmap-ncat # Fedora/CentOS/RHEL
sudo apt install ncat # Debian/Ubuntu
ncat -klp 8080 --send-only -c 'printf "HTTP/1.1 200 OK\n\n%s" "<html><body><h1>Terminal Tuesday !!</h1></body></html>";'
# (Ctrl-C to quit)
# Options in use:
# -k, --keep-open Accept multiple connections in listen mode
# -l, --listen Bind and listen for incoming connections
# -p, --source-port port Specify source port to use
# --send-only Only send data, ignoring received; quit on EOF
# -c, --sh-exec <command> Executes the given command via /bin/sh
# Test in another terminal or Web browser:
curl http://127.0.0.1:8080
curl http://YOUR_LAN_OR_PUBLIC_IP:8080
Other Examples:
Similar to rm -rf
these scripts are a great way to get yourself in trouble. Close off port 8080 on your firewall before playing, think before you type, know that any website can send you to a local address like 127.0.0.1:8080 in a hidden iframe and consider security is hard even for sysadmins.
# Introducing option: --allow <host>
# Allow only given hosts to connect to Ncat
# Return all environment variables accessible to the process (yikes)
ncat -klp 8080 --allow 127.0.0.1 --send-only -c 'printf "HTTP/1.1 200 OK\n\n"; printenv;'
# Run a local script and return the output (yikes)
ncat -klp 8080 --allow 127.0.0.1 --send-only -c 'printf "HTTP/1.1 200 OK\n\n"; ./run.sh;'
# Return an HTML page
ncat -klp 8080 --send-only -c 'printf "HTTP/1.1 200 OK\n\n"; cat ./index.html;'
# IP Lookup Server:
ncat -klp 8080 --send-only -c 'printf "HTTP/1.1 200 OK\n\n%s" "$NCAT_REMOTE_ADDR";'
# More ncat env variables: https://secwiki.org/w/Ncat/Environment_variables
Bash can look very sexy sometimes:
for n in {1..5}; do echo $n; done
And the output:
1
2
3
4
5
Something I learned recentlyβ¦
If any amount of leading zeros are added, the output will always be equal to the maximum number of possible digits.
for n in {01..10000}; do echo $n; done
00001
00002
β¦
09999
10000
for n in {1..010000}; do echo $n; done
000001
000002
β¦
009999
010000
This might be the coolest usage of an old terminal with a modern Linux machine I have yet seen. This is purely entertainment but something I have watched more than once, purely for the smiles.
This is part 2 where it is actually being employed.
For what it is worth, he is using Kubuntu for his Linux distribution.
Because it isnβt nice to just post a part 2, here is the Part 1
If this worked with Fusion 360β¦ (insert dream sequence here)β¦
If Fusion360 was fully supported on Linux by Autodeskβ¦ (insert dream sequence here)β¦
That video made me want to check out openSCAD, any time I can do something from the terminal Iβm all in. So far it is really neat. Thanks for sharing!
I think I need to try to understand openSCAD too. Itβs on the listβ¦ but not high on the list.
I use openSCAD β it just requires you to break an object down into shapes that produce the whole. I think one of the best features of it is that itβs parametric. You can search scad files on thingiverse β I have discovered some mind-blowing mathematical functions β which reminded me that there are people in the world who have forgotten more math than I have ever known.
what does a simple part, like a screw, look like? or a keyboard cap for example?
Iβll first show you the simplicity of the code with this very simple design of a push pin for the plastic inner fender for a car. The parametric nature of openSCAD allows me to return to the code and change a variable easily and with great precision.
// Push Rivet for Car
// Mark Cain
// August 22, 2020
// Revision 0.3 //
// Revision 0.2 // measurements in mm height=16, cone_base=14, cone_tip=6
// Revision 0.1 // measurements in mm height=20, cone_base=15, cone_tip=5
cone_base = 14;
cone_height = 16;
cone_tip = 6;
ring_elevation = 6;
ring_height = 4;
connector_height =10;
rotate([0, 0 , 0]){
difference() {
union() { // beginning of the visible union
cylinder(h=cone_height, d1=cone_base, d2=cone_tip);
}; // end of the visible union
union() { // beginning of the cut union
translate([0, 0, ring_elevation])
cylinder(h=ring_height, d=cone_base);
}; // end of the cut union
}; // end of project difference
// inside column for the ring's center
cylinder(h=connector_height, d=cone_tip+2);
}; // end of rotate
// variables
$fn=360;
Here is a more complex part β an auger that I incorporated into a design Iβm making. For complex models like this, I rely on finding these functions online.
// Parametric Printable Auger
// It is licensed under the Creative Commons - GNU GPL license.
// 2013 by William Gibson
// http://www.thingiverse.com/thing:96462
use <utils/build_plate.scad>
////////////
//Examples//
////////////
//Simple Example
// auger(r1=1/8*inch, r2=.75*inch, h=1*inch,
// turns=2, multiStart=1, flightThickness = 0.6,
// overhangAngle=20, supportThickness=0.0);
//Multistart example
// auger(r1=1/2*inch, r2=2*inch, h=2*inch,
// turns=1, multiStart=3, flightThickness = 0.6,
// overhangAngle=20, supportThickness=0.0);
//Support
// auger(r1=1/2*inch, r2=2*inch, h=2*inch,
// turns=2, multiStart=1, flightThickness = 0.6,
// overhangAngle=10, supportThickness=0.4);
//////////////////////
//CUSTOMIZER OPTIONS//
//////////////////////
/* [Auger] */
//The total amount of twist, in degrees
Auger_twist = 877; //[90:1080]
//The radius of the auger's "flight" past the shaft
Auger_flight_radius = 20; //[5:50]
//The number of "flights"
Auger_num_flights = 2; //[1:5]
//The height, from top to bottom of the "shaft"
Auger_flight_length = 100; //[10:200]
/* [Printer] */
//The overhang angle your printer is capable of
Printer_overhang_capability = 20; //[0:40]
//The thickness of perimeter support material
Auger_perimeter_thickness = 0.0; //[0:None, 0.8:Thin, 2:Thick]
/* [Uninteresting] */
//The radius of the auger's "shaft"
Auger_shaft_radius = 10; //[1:25]
//The thickness of the "flight" (in the direction of height)
Auger_flight_thickness = 1; //[0.2:Thin, 1:Medium, 10:Thick]
Auger_handedness = "right"; //["right":Right, "left":Left]
/* [Build plate] */
//for display only, doesn't contribute to final object
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
//when Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 200; //[100:400]
//when Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 200; //[100:400]
build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);
/* [Hidden] */
M_PI = 3.14159;
mm = 1;
inch = 25.4 * mm;
auger(
r1 = Auger_shaft_radius,
r2 = Auger_shaft_radius + Auger_flight_radius,
h = Auger_flight_length,
overhangAngle = Printer_overhang_capability,
multiStart = Auger_num_flights,
flightThickness = Auger_flight_thickness,
turns = Auger_twist/360,
pitch=0,
supportThickness = Auger_perimeter_thickness,
handedness=Auger_handedness,
//$fn=50,
$fa=12,
$fs=5
);
//////////////////////
//Auger Library Code//
//////////////////////
//Notes:
//Specify 'pitch' OR 'turns' (pitch overrides turns)
//r1 >= 1mm please
//flightThickness >= extrusion thickness of your printer
//supportThickness >= 2 * extrusion width of your printer, or zero to turn off.
module auger(r1 = 0.5*inch, r2 = 0.75*inch, h=1*inch, multiStart=1,
turns=1, pitch=0,
flightThickness = 0.2*mm, overhangAngle=20, supportThickness=0*mm,
handedness="right" /*"left"*/)
{
assign(_turns = ((pitch>0?h/(pitch+flightThickness):turns)))
{
if(pitch != 0)
{
echo("Pitch defined - ignoring turns parameter");
//Each 1 turn is a height of (pitch+flightThickness)
//A height of h will make x turns where x = h / (pitch+flightThickness)
echo("Calculated turns = ", _turns);
}
else
{
if(turns < 0)
{
echo("ERROR: Cannot handle negative turns. Use handedness='left' instead to reverse rotation.");
}
}
assign(extraFlight = tan(overhangAngle)*(r2-r1))
{
difference()
{
auger_not_truncated(r1=r1, r2=r2, h=h, turns=_turns,
flightThickness=flightThickness, overhangAngle=overhangAngle,
multiStart=multiStart, supportThickness=supportThickness,
handedness=handedness=="right"?1:-1);
//Cut off bottom of auger so it's printable.
translate([0,0,-extraFlight])
cube([r2 * 3,r2 * 3,2*extraFlight], center=true);
}
}
}
}
module auger_not_truncated(r1 = 0.5*inch, r2 = 0.75*inch, h=1*inch, turns=1, flightThickness = 0.2*mm, overhangAngle=20, multiStart=1, supportThickness=0*mm, handedness=1)
{
assign(extraFlight = tan(overhangAngle)*(r2-r1))
{
if(supportThickness > 0)
{
difference()
{
cylinder(h=h, r=r2+0.1, $fs=0.5);
translate([0,0,-1])
cylinder(h=h+2, r=r2-supportThickness+0.1, $fs=0.5);
}
}
cylinder(r=r1, h=h,$fs=0.5); //Central shaft
for(start=[1:1:multiStart]) //render each flight
{
rotate([0,0,handedness*360*(start-1)/multiStart])
augerFlight(flightThickness=flightThickness, turns=turns, rHidden=(r1>6?r1-5:1), r1=r1, r2=r2, h=h, extraFlight=extraFlight, handedness=handedness);
}
}
}
module augerFlight(flightThickness, turns, rHidden, r1, r2, h, extraFlight, handedness)
{
if($fs < 0.1)
{
echo("WARNING: $fs too small - clamping to 0.1");
}
if($fa < 0.1)
{
echo("WARNING: $fa too small - clamping to 0.1");
}
//Calculate numSteps based on $fn, $fs, $fa
assign($fs = max(0.1, $fs), $fa = max(0.1, $fa),
numSteps=($fn > 0.0) ? $fn :
max(5,
max(h/(max($fs,0.1)),
max(360.0 * turns / $fa,
r2*2*M_PI*turns / max($fs,0.1)))))
{
echo("Number of Steps calculations:");
echo("minimum",5);
echo("height step", h/(max($fs,0.1)));
echo("angle", 360.0 * turns / $fa);
echo("perimeter size", r2*2*M_PI*turns / max($fs,0.1));
echo("numSteps = maximum: ", numSteps);
assign(heightStep=((h-(flightThickness))/numSteps))
{
translate([0,0,-extraFlight]) //Move down so the extraFlight material is below z=0
{
for(step=[0:1:numSteps-1]) //For each step in a flight
{
rotate([0,0,handedness*turns*step/numSteps*360])
translate([0,0,heightStep*step])
if(handedness==1)
augerPolyhedron(flightThickness=flightThickness, extraFlight=extraFlight, rHidden=rHidden, r1=r1, r2=r2, turns=turns, numSteps=numSteps, heightStep=heightStep);
else
mirror([1,0,0])
augerPolyhedron(flightThickness=flightThickness, extraFlight=extraFlight, rHidden=rHidden, r1=r1, r2=r2, turns=turns, numSteps=numSteps, heightStep=heightStep);
}
}
}
}
module augerPolyhedron(flightThickness, extraFlight, rHidden, r1, r2, turns, numSteps, heightStep)
{
//_1 is first angle, _2 is second angle
//_I is inside, _O is outside
assign(top_1_I=flightThickness+extraFlight, bot_1_I=0,
top_1_O=flightThickness+extraFlight, bot_1_O=extraFlight,
degOverlap=0.1,
rHiddenCorrection=(r1-rHidden)/(r2-r1)
)
{
//echo(rHidden, r1, r2);
//echo("rHiddenCorrection=",rHiddenCorrection);
//echo("rHiddenCorrection*extraFlight=",rHiddenCorrection*extraFlight);
//echo("heightStep=",heightStep);
polyhedron(
points=[
[0,rHidden,bot_1_I-rHiddenCorrection*extraFlight], //0
[0,rHidden,top_1_I], //1
[0,r2, bot_1_O], //2
[0,r2, top_1_O], //3
[-rHidden*sin(360*turns/numSteps+degOverlap), //4
rHidden*cos(360*turns/numSteps+degOverlap),
bot_1_I+heightStep-rHiddenCorrection*extraFlight], //+rHiddenCorrection*heightStep-rHiddenCorrection*extraFlight],
//
[-rHidden*sin(360*turns/numSteps+degOverlap), //5
rHidden*cos(360*turns/numSteps+degOverlap),
top_1_I+heightStep],
[-r2*sin(360*turns/numSteps+degOverlap), //6
r2*cos(360*turns/numSteps+degOverlap),
bot_1_O+heightStep],
[-r2*sin(360*turns/numSteps+degOverlap), //7
r2*cos(360*turns/numSteps+degOverlap),
top_1_O+heightStep]
],
triangles=[
[0,1,2], //"triangle" 1
[2,1,3],
[4,6,5], //"triangle" 2
[6,7,5],
[1,4,5],
[1,0,4], //Inner "square"
[3,7,6],
[3,6,2], //Outer "square"
[0,2,4],
[4,2,6], //Bottom "square"
[1,5,3],
[5,7,3], //Top "square"
]);
}
}
module augerPolyhedronBackup(flightThickness, extraFlight, r1, r2, turns, numSteps, heightStep)
{
//_1 is first angle, _2 is second angle
//_I is inside, _O is outside
assign(top_1_I=flightThickness+extraFlight, bot_1_I=0, top_1_O=flightThickness+extraFlight, bot_1_O=extraFlight, degOverlap=0.1)
{
polyhedron(
points=[
[0,r1,bot_1_I], //0
[0,r1,top_1_I], //1
[0,r2, bot_1_O], //2
[0,r2, top_1_O], //3
[-r1*sin(360*turns/numSteps+degOverlap), //4
r1*cos(360*turns/numSteps+degOverlap),
bot_1_I+heightStep],
[-r1*sin(360*turns/numSteps+degOverlap), //5
r1*cos(360*turns/numSteps+degOverlap),
top_1_I+heightStep],
[-r2*sin(360*turns/numSteps+degOverlap), //6
r2*cos(360*turns/numSteps+degOverlap),
bot_1_O+heightStep],
[-r2*sin(360*turns/numSteps+degOverlap), //7
r2*cos(360*turns/numSteps+degOverlap),
top_1_O+heightStep]
],
triangles=[
[0,1,2], //"triangle" 1
[2,1,3],
[4,6,5], //"triangle" 2
[6,7,5],
[1,4,5],
[1,0,4], //Inner "square"
[3,7,6],
[3,6,2], //Outer "square"
[0,2,4],
[4,2,6], //Bottom "square"
[1,5,3],
[5,7,3], //Top "square"
]);
}
}
}
Clean old configuration files from your Debian based system:
apt purge $(dpkg -l | awk '/^rc/ { print $2 }')
It is very useful after doing an in-place release upgrade. Though be careful and read the output before proceeding.
These are pretty cool and def above my head, Thank you!
Really cool. We need to move this to a dedicated SCAD thread.
watch
Execute a program periodically. Can be handy for working on scripts.
# Display date & time each second:
watch --no-title date
# Display output of a script every 0.5 seconds:
watch --interval 0.5 --no-title ./MY_PROJECT.sh
Usage:
watch [options] command
Options:
-b, --beep beep if command has a non-zero exit
-c, --color interpret ANSI color and style sequences
-d, --differences[=<permanent>]
highlight changes between updates
-e, --errexit exit if command has a non-zero exit
-g, --chgexit exit when output from command changes
-n, --interval <secs> seconds to wait between updates
-p, --precise attempt run command in precise intervals
-t, --no-title turn off header
-x, --exec pass command to exec instead of "sh -c"
-h, --help display this help and exit
-v, --version output version information and exit