#ScriptSaturday with enough alias magic, bash can have real try-catch syntax!

libsh module here; BSD-3 :)

As part of my de-Google efforts I'm investigating file manager scripts for my shared hosting space. Tiny File Manager is a great little script, one php file in the home directory with hash password login, and everything is accessible. Create, edit, view, delete, upload, copy and link to. I'll test other free/OS scripts but this is great, and obv you can customise the script as much as you like.

#storage #php #scripts #scriptsaturday #degoogle

https://tinyfilemanager.github.io/

Tiny File Manager

Web based File Manager in PHP, Manage your files efficiently and easily with Tiny File Manager

#ScriptSaturday From things that are a bit less bonkers (but just a bit): if need be, aliases can be used to “modify” parts of the language:

#!/bin/bash shopt -s expand_aliases alias uwu='a() {' owo='}'; uwu echo meow; owo # defines a function called `a`, of course a

This unfortunately has some limitations: only zeroeth argument gets considered for expansion, and you can’t define aliases with special characters.

#ScriptSaturday If that wasn’t enough quirks and gotchas - alias expansion works on a line-by-line basis. That is, the following example will not work:

#!/bin/bash shopt -s expand_aliases alias meow='echo meow'; meow

… but this one will work just fine …

#!/bin/bash shopt -s expand_aliases alias meow='echo meow' meow

The only difference between the two of them is a newline between the alias definition and the call. Finally, we found the limit of bash oneliners!

#ScriptSaturday Aliases are quite weird!

In Bash, the term alias is a close relative to a #define in C/C++. However, as expansion happens at definition time, but aliasing happens at runtime, the behavior in functions may be a bit unexpected:

#!/bin/bash shopt -s expand_aliases alias meow='echo meow' a() { meow } alias meow='echo nyaa' meow # outputs "nyaa" a # outputs "meow"

Likewise, when aliasing within a function, it will set an alias in the global scope, which won’t affect the already expanded aliases inside the current function. This can be visualized with declare -f:

#!/bin/bash shopt -s expand_aliases alias meow='echo meow' a() { alias meow='echo nyaa' meow } declare -f a # shows `echo meow` inside the function, not `meow`

Furthermore, alias expansion is disabled by default in scripts. If tried anyways, it will fail silently - or, rather, it will set an alias (to be used if expand_aliases ever gets enabled). Bash doesn’t catch this even with set -e enabled, so it seems like there isn’t a good way to catch those errors without parsing output of shopt.

set -e shopt -u expand_aliases # disable alias expansion alias ls='ls -la' # returns 0 ls # still not ls -la

#ScriptSaturday Did You Know: in bash, continue and break accept a parameter to control how many loops to fall through?

continue: continue [n] Resume for, while, or until loops. Resumes the next iteration of the enclosing FOR, WHILE or UNTIL loop. If N is specified, resumes the Nth enclosing loop. (...)

It’s useful if you need to conditionally break through multiple loops at once. I actually used it earlier last week in one place within notORM, which saved me from using an awkward flag variable!

The following snippet will only display 1 thru 10 once, instead of 10 times as with normal continue:

for i in {1..10}; do for j in {1..10}; do echo $i continue 2 done done

On the other hand, if expects an exact syntax of if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi. This requires us to always use then, but.. who said we have to use if?

This is also valid Bash! Just replace false with your own expr or command 

[[ false ]] && { echo meow } || { echo nyaa }

(note: the “else” case will execute both if the condition returns false, as well as if the first { } returns false, which is probably not what you want. treat this as a neat oddity.)

#ScriptSaturday

#ScriptSaturday Did You Know: in bash, with for loops, you can skip the do (...); done syntax and use { } instead? Check this out  

for i in 1 2 3; { echo $i; }

Completely valid bash! Unfortunately, same thing doesn’t work with while :(

Best you can do to make whiles shorter is while :; do ...; done (replacing usual true with :, which is functionally equivalent) or use for ((;;)) { ...; } instead.

Thanks to spiderella on ##bash-crimes @ irc.libera.chat for the tip!

One Bash fallacy I found myself to believe for whatever reason is that you can’t do substitutions on the same variable you’re assigning to. This used to lead me towards using multiple variables to hold an intermediate value, which in itself contributed to code clutter. This is, fortunately, not needed!

Remember: while you can’t do cat a | grep meow > a (cat abuse for demonstrative purposes only), nothing stops you from doing a=${a/nyaa/meow}!

Using this, one can create expression chains of sort, which only operate on one variable:

delim=$'\01' newline=$'\02' ctrl=$'\03' # (...) tr="${1//$delim}" # remove 0x01 tr="${tr//$newline}" # remove 0x02 tr="${tr//$ctrl}" # remove 0x03 tr="${tr//$'\n'/$newline}" # \n -> 0x02

(excerpt from notORM) #ScriptSaturday

http.sh/src/notORM.sh at cd0fe42879a9012dbe5caab5c9a7624def8ef5a5

http.sh - A webserver/web framework written entirely in Bash. Fully configurable, with SSL support, vhosts and many other features.

the sakamoto git server

one of the examples on the bash-hackers page on substitutions is very relatable

#ScriptSaturday (okay i promise that’s the last one today)

Parameter expansion [Bash Hackers Wiki]