Have taken up farming.
dylanaraps / pure-bash-bible Goto Github PK
View Code? Open in Web Editor NEWπ A collection of pure bash alternatives to external processes.
License: MIT License
π A collection of pure bash alternatives to external processes.
License: MIT License
Have taken up farming.
function exit_trap() {
local lc="$BASH_COMMAND" rc=$?
echo "Command [$lc] exited with code [$rc]"
}
trap exit_trap err
There are a ton of obsolete code samples in nearly every programming language.
There is no main reason. $()
is new `` is old.
`` vs $()
# Sample
var=`something` vs var=$()
Use always #!/usr/bin/env because
#!/usr/bin/env
searches the path of bash. Not on all linux permutations the default bash location is /bin/bash so always use that.
#!/usr/bin/env vs #!/bin/bash
#Sample
#!/usr/bin/env
The function
key word is not required any more. Don't use that because it reduced the compatibility of different bash versions.
function do_something () {
}
# vs
do_something () {
}
These are only few examples I have a lot more if you want I could submit a PR.
Regards
hello:
I am an Operations Engineer from China.A few days ago,I saw your open source project in github,pure-bash-bible.
I often write some Linux scripts for working.Thank you for opening up your project.It is very helpful for my work.
But its language is English,So I translated it into Chinese,i think It would be helpful for Chinese engineers, I think I should tell you this to get your support.Thank you for your work again.My repository is https://github.com/A-BenMao/pure-bash-bible-zh_CN
I'm trying to PR a working curl / wget (using /dev/tcp)
set -ex
function __curl() {
read proto server path <<<$(echo ${1//// })
DOC=/${path// //}
HOST=${server//:*}
PORT=${server//*:}
[[ x"${HOST}" == x"${PORT}" ]] && PORT=80
exec 3<>/dev/tcp/${HOST}/$PORT
echo -en "GET ${DOC} HTTP/1.0\r\nHost: ${HOST}\r\n\r\n" >&3
(while read line; do
[[ "$line" == $'\r' ]] && break
done && cat) <&3
exec 3>&-
}
__curl http://www.google.com/favicon.ico > mine.ico
md5sum mine.ico
Yet 'im stuck on the && cat to handle the file body (binary)
I'm sure i can use a new file descriptor && echo , but my bash skill ends here π
I can link & use a pure bash [1], yet i'm sure there is something more elegant to do here.
This shouldn't be as hard as dirname; trailing slashes are the crux in the first counter-examples, then there is lack of support for the second optional argument, which nobody knows about anyway:
~ $ basename_pbb() {
# Usage: basename "path"
: "${1%/}"
printf '%s\n' "${_##*/}"
}
~ $ basename_pbb something//
~ $ basename something//
something
~ $ basename_pbb /
~ $ basename /
/
~ $ basename_pbb dir/something thing
something
~$ basename dir/something thing
some
The POSIX standard helps, https://pubs.opengroup.org/onlinepubs/9699919799/
According to this, some compliant implementation could go like this:
/
characters, the result is a single /
character./
characters in string, they shall be removed./
characters remaining in string, the prefix of string up to and including the last /
character in string shall be removed.Meaning that this implementation is spec-conformant:
basename_pbb() {
# Usage: basename "path" ["suffix"]
local tmp
if [[ $1 =~ ^/+$ ]]; then
tmp=/
else
tmp=${1%"${1##*[!/]}"} # $1 without trailing slashes
tmp=${tmp##*/} # $tmp without its longest prefix ending in a slash
fi
[[ $tmp != "$2" ]] && tmp=${tmp%"$2"}
printf '%s\n' "$tmp"
}
If you want to have fun codegolfing it, be my guest! But don't remove the quotes around $2
, because the asterisk and question mark characters are legal in filenames.
BTW, as for unit tests, you could also use @kward's https://github.com/kward/shunit2.
lines() {
# Usage: lines "file"
mapfile -tn 0 lines <<<"$1"
# instead of < "$1"
printf '%s\n' "${#lines[@]}"
}
Include prepend
function as described here.
prepend() {
printf '%s%s' "$1" "$(< "$2")" > "$2"
}
Hey,
I just want to spread love and say thank you for this awesome tutorial. Keep it up and hope you will continue this project.
Regards,
deltax
Would be neat to have a file that could be included from a .bashrc
(e.g. source ~/.bashrc-bash-bible
). This could also be useful to reproducibility with test.sh
In Bash 5.0 scripts, BASH_ARGV
also gets the current function's arguments when extdebug
is enabled for the first time, so reverse_array
's results are printed twice. Relevant section of the changelog:
The shell doesn't automatically set BASH_ARGC and BASH_ARGV at startup unless it's in debugging mode, as the documentation has always said, but will dynamically create them if a script references them at the top level without having enabled debugging mode.
Compatibility with the old version is available with the compat44
shell option.
lines function could be easily changed to read from variable, file ( as it does ) and a pipe output. Here is the code that works for me:
Usage: lines < "file" or lines <<< "$variable" or echo something | lines
lines() {
mapfile -tn 0 lines
printf '%s\n' "${#lines[@]}"
}
Similar issue that was closed: #85
https://github.com/dylanaraps/pure-bash-bible#split-a-string-on-a-delimiter
The -a
option for read
isn't available in every version of bash. At a minimum, it doesn't work on macOS's built-in bash (on macOS High Sierra, I'm running bash 3.2.57
).
This does work if I upgrade to bash 4+.
For what it's worth, I suspect there may be an alternative pure bash method to split a string into an array, but I haven't been able to come up with a perfectly generic function yet. Related: this is a fun read
ord is usually handled by the external "od" command.
chr() {
[ "$1" -lt 256 ] || return 1
printf "\\$(printf '%03o' "$1")"
}
ord() {
LC_CTYPE=C printf '%d' "'$1"
}
What's the purpose of these instructions : "${chapter[$i]/$'\n'*}"; : "${_/\# }"; : "${_,,}"
in build.sh? I run bash build.sh
without them in ubuntu18.04.2 TLS x86_64, it get the same result.
#!/usr/bin/env bash
#
# Turn the single document bible into a book separated by chapters.
main() {
rm -rf manuscript
mkdir -p manuscript
# Split the README.md into chapters based on markers.
while IFS=$'\n' read -r line; do
[[ "$chap" ]] && chapter[$i]+="$line"$'\n'
[[ "$line" == "<!-- CHAPTER START -->" ]] && chap=1
[[ "$line" == "<!-- CHAPTER END -->" ]] && { chap=; ((i++)); }
done < README.md
# Write the chapters to separate files.
for i in "${!chapter[@]}"; do
: "${chapter[$i]/$'\n'*}"; : "${_/\# }"; : "${_,,}"
printf '%s\n' "${chapter[$i]}" > "manuscript/chapter${i}.txt"
printf '%s\n' "chapter${i}.txt" >> "manuscript/Book.txt"
done
}
main
I'm not sure if this was added later as the second example isn't sorted, but on my bash
4.4 and 5.0, associative arrays sort in lexicographical and reverse lexicographical order respectively. Strangely, I don't see the change in the 5.0 changelog, though.
What is the difference between this repo and mihirk/pure-bash-bible ?
In the "Bypass shell aliases and functions", the trick of using a backslash does not work for functions \
(though it does work for aliases).
This is on my Mac running
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
Copyright (C) 2007 Free Software Foundation, Inc.
e.g.
$ logname
sal
$ function logname() { echo 'There is no logname, only Zuul.'; }
$ logname
There is no logname, only Zuul.
$ \logname
There is no logname, only Zuul.
Instead, you can use
$ $(which logname)
sal
or possibly with -f
added
see https://salferrarello.com/command-line-which-wrong-result/#skipping-aliases-and-functions for more information.
Just a bit of forewarning with regard to the "recommended" use of shebangs.
# Right:
#!/usr/bin/env bash
# Less right:
#!/bin/bash
Using env by default is not a good habit because as you state above:
The former searches the user's PATH to find the bash binary.
You do not WANT bash looking at a users local path for say, system scripts, things meant to be run by root or any other user. To say the latter is bad, is.. well, bad itself.
PATH can be manipulated, a script can be set readonly.
Took an inordinate amount of time to explain this to a junior sysadmin who was adamant every script on a production host should be changed "because".
Wiki pages can be created by separating the important parts instead of the too long readme file. I think that's absolutely necessary for better understanding and life-saving. :)
Thanks!
First of all thanks for this amazing repository, it's hard to come by a lot of useful tips like this.
I would like to contribute with some of my own personal code, if it's something that interests you.
A redis bash "client library" using only bash internals.
Which BTW I'm sure I can incorporate some improvements from your document.
Another is some random functions from this repository:
Most of the functions on this file only use bash internals, and they are used to manipulate ip addresses, like for example to determine if an IP address belongs to a CIDR block.
If you're interested please let me know if you would like me to do a PR or if you prefer to do it yourself.
Also, these 2 repositories are examples of /dev/tcp usage and bitwise manipulation using bash
Thanks in advance.
Hi,
first big thanks for the "bible".
I think it might helpful to add more file handling tips like "get filepath from first file in a directory" or "get the filepath for the last modified file in a directory".
Would someone mind to add good solutions to these?
Best regards
Hi, I don't have time to do a fork and make a pull request, but here is code which might interest you, it provide an alternative to uuid -v4 (or to /proc/sys/kernel/random/uuid )
# generate an uuid -v4 using pure bash
function uuidBash ()
{
local N B C='89ab'
for (( N=0; N < 16; ++N ))
do
B=$(( $RANDOM%256 ))
case $N in
6)
printf '4%x' $(( B%16 ))
;;
8)
printf '%c%x' ${C:$RANDOM%${#C}:1} $(( B%16 ))
;;
3 | 5 | 7 | 9)
printf '%02x-' $B
;;
*)
printf '%02x' $B
;;
esac
done
}
When a trap on DEBUG is set, $_
will always be the last argument of the last trap command, making it unusable. This could be noted somewhere in the README π
Two small issues on this example:
arrray[@]
instead of array[*]
printf receives multiple arguments, that means that when trying to add another argument to the printf this can get quite confusing, for instance when adding the number of args:shopt -s extdebug
reverse_array() {
printf 'Reversed args: %s\nNumber of args: %s\n' "${BASH_ARGV[@]}" "$#"
}
reverse_array 1 2 3
Will output the following:
Reversed args: 3
Number of args: 2
Reversed args: 1
Number of args: 3
BASH_ARGV
also contains the script arguments (and other potential caller arguments), that means that this example only works in a specific case, for instance if the function call reverse array 1 2 3
is inside the script foo
, calling foo a b c
from a shell:Will output:
3
2
1
c
b
a
So if that sounds good I would go with the following:
reverse_array() {
shopt -s extdebug
f() { printf '%s\n' "${BASH_ARGV[*]:0:$#}"; }; f "$@"
shopt -u extdebug
}
reverse_array 1 2 3
With the following output:
3 2 1
This is very likely not perfect but it seems to pass any RGB color I throw at it.
# rgb2hsl - usage: rgb2hsl r g b
rgb_to_hsl() {
local r g b
local h s l
local min max
# Ensure input is greater than 0.
((r=r < 0 ? 0 : ${1:-0}))
((g=g < 0 ? 0 : ${2:-0}))
((b=b < 0 ? 0 : ${3:-0}))
# Ensure input is lesser than 255.
((r=r > 255 ? 255 : r))
((g=g > 255 ? 255 : g))
((b=b > 255 ? 255 : b))
# Convert RGB value to a 0-100 range.
#
# This is usually a 0-1 range but we
# multiply by 100 to "fake" floating
# point.
((r=r * 100 / 255))
((g=g * 100 / 255))
((b=b * 100 / 255))
# Give the min variable the maximum
# possible value. We must set it to
# something.
((min=255))
# Find the minimum and maximum RGB
# values for use below.
((min=r < min ? r : min))
((max=r > max ? r : max))
((min=g < min ? g : min))
((max=g > max ? g : max))
((min=b < min ? b : min))
((max=b > max ? b : max))
# Calculate the luminace using the
# above values.
((l=(min + max) / 2))
# Calculate the saturation using a
# different formula based on its
# value.
#
# Again, we multiply the values by
# 100 to "fake" floating points.
((s=min == max
? 0
: l < 50
? (max - min) * 100 / (max + min)
: (max - min) * 100 / (200 - max - min)
))
# Calculate the hue based on which
# RGB value is the maximum.
((h=s == 0 ? 0
: r == max
? (g - b) * 100 / (max - min)
: g == max
? 200 + (b - r) * 100 / (max - min)
: b == max
? 400 + (r - g) * 100 / (max - min)
: 0
))
# Convert the calculation result into
# degrees. Divide by 100 to reverse the
# floating point hacks.
((h=h * 60 / 100))
printf '%s\n' "$h $s $l"
}
rgb_to_hsl 18 233 45
With bash version 4+ and associative arrays, it's possible to create multidimensional arrays. Here an example:
#written on phone and untested, apologies for mistakes!
declare -A animals
animalNames=()
addAnimal() {
animalNames+=("$1")
animals["$1_species"]="$2"
animals["$1_age"]="$3"
animals["$1_sound]="$4"
}
addAnimal Milo cat 8 meow
addAnimal Rex dog 2 woof
for animal in $animalNames; do
echo "$animal is a ${animals[$animal_species]}, it's ${animals[$animal_age]} years old and makes ${animals[$animal_sound]}!"
done
Would you be interested in adding this (and optionally some more helperfunctions) to the bible? I could create a PR in the next few weeks
It would be awesome to see this in a gitbook format. WIth the existing travis integration, it should be pretty easy to do so. Makes the doc nice and searchable then.
Here are a few counter-examples:
~ $ dirname_pbb() {
# Usage: dirname "path"
printf '%s\n' "${1%/*}/"
}
~ $ dirname dir1/dir2/
dir1
~ $ dirname_pbb dir1/dir2/
dir1/dir2
~ $ dirname dir1/dir2
dir1
~ $ dirname_pbb dir1/dir2
dir1/
~ $ dirname dir1
.
~ $ dirname_pbb dir1
dir1/
In fact I already tried to reimplement that one with variable substitution and found a few corner cases like this, making me unsure I was up to the task without a serious read of⦠the POSIX spec, I suppose.
CONTRIBUTING.md
.shopt
and set
./dev/tcp
pdf
Hi,
Just noticed this repo and thought I would share this function that I came up with a few days ago:
function random_array_element {
declare -n array="$1" # namerefs are cool! And it even works with the array being a local variable in another function!
local length=${#array[@]}
local index=$(( RANDOM % length))
local choice=${array[$index]}
echo "$choice"
}
Used like:
function choose_icon {
# Return random icon.
local icons=(
/usr/share/icons/oxygen/22x22/emotes/face-embarrassed.png
/usr/share/icons/oxygen/22x22/emotes/face-foot-in-mouth.png
/usr/share/icons/oxygen/22x22/emotes/face-clown.png
/usr/share/icons/oxygen/22x22/emotes/face-confused.png
/usr/share/icons/oxygen/22x22/emotes/face-crying.png
/usr/share/icons/oxygen/22x22/emotes/face-laugh.png
/usr/share/icons/oxygen/22x22/emotes/face-laughing.png
/usr/share/icons/oxygen/22x22/emotes/face-quiet.png
/usr/share/icons/oxygen/22x22/emotes/face-raspberry.png
/usr/share/icons/oxygen/22x22/emotes/face-sad.png
/usr/share/icons/oxygen/22x22/emotes/face-sleeping.png
/usr/share/icons/oxygen/22x22/emotes/face-smirk.png
/usr/share/icons/oxygen/22x22/emotes/face-surprise.png
/usr/share/icons/oxygen/22x22/emotes/face-uncertain.png
/usr/share/icons/oxygen/22x22/emotes/face-wink.png
/usr/share/icons/oxygen/22x22/emotes/face-worried.png
/usr/share/icons/oxygen/22x22/emotes/face-yawn.png
/usr/share/icons/oxygen/22x22/emotes/food-cake.png
/usr/share/icons/oxygen/22x22/emotes/food-pizza.png
)
echo $(random_array_element icons)
}
choose_icon # => "/usr/share/icons/oxygen/22x22/emotes/food-cake.png"
I want to copy this line to my script:
file_data="$(<"file")"
Should I now add LICENSE.md
from the root of this repository to my snippet?
Of the three examples here only hash
correctly reports if an executable is in the PATH. type -p
and command -v
both report success on aliases, builtins, etc.
type -P
(capital P) does correctly report if an executable is in the PATH.
Perhaps this section could be reworded to make this distinction more clear?
Indeed, IFS=$'\n' read -d "" -ra file_data < "file"
will discard empty lines!
I saw that at the very bottom of the README
there was a command to run files in the background, which is very handy. However, I was wondering if there is a way to also run a certain function in the background?
This repository has received 700~ stars in the last 1-2 weeks and yet it is no where to be found on GitHub's trending page. I don't know if this is somehow related to the large influx of Chinese users who have recently found this repository.
Can anyone else confirm or reproduce what I am seeing?
Source for prior star count (Wayback machine, see star count in "inspect element"):
Hey,
I was wondering that this is an infinite loop.
for((;;)){ echo hi;}
Can you explain me how bash interpret this?
Regards,
deltax
When I saw your repo I immediately thought of this project: https://github.com/bashup/realpaths
This is up to you whether to include it in the README, of course.
Tagging @pjeby, the author π
I am fond of this:
HELP_TEXT="Usage: [-f|--foo-bar] [-o] baz"
for i in "$@"
do
case $i in
-f=*|--foo-bar=*)
FOOBAR=" --other-scripts-flag=${i#*=}"
shift # past argument=value
;;
-o)
OTHER=" -o"
shift # past argument=value
;;
*)
echo "$HELP_TEXT"
exit 1
;;
esac
done
./other_script.sh${FOOBAR}${OTHER}
I got this from https://stackoverflow.com/a/14203146/881224
Hey @dylanaraps, I think is super useful, thanks for making it! I was wondering if you could add a bit on your rationale for using #!/usr/l/bin/env bash
instead. This is generally my thoughts: https://stackoverflow.com/a/55927235, but I'm not a bash expert.
First of all, thanks for the great work.
I just wanted to let you know that I had built a UI around the content that is in the README.md
.
I kinda thought it would be a great idea to have a searchable web interface around the content.
The project is https://github.com/meain/pbbui and I have hosted it currently on github at https://meain.github.io/pbbui/
Can we use bash regex to match on multiple lines ?
For ex,
hello: one
hello: 2
Something: four
random: file
I want to extract just hello
lines without grep, this may sound unnecessary, but just finding a way for the curious mind. π
Edit: Obviously without running a loop
I've started work on an alternative version of the "pure bash bible" for pure POSIX sh
snippets.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.