Terminal Takeaway 🥡

Boxing Text

These are some improvements to a cool text boxing script made by TFL. To grok it better check out the original script lesson here which is easier to parse.

Did you know printf can produce variables!? Now we know :stuck_out_tongue:

printf -v MyPrintFVariable 'wow'
printf '%s it works!\n' "$MyPrintFVariable"
echo "$MyPrintFVariable not just inside printf"
wow it works!
wow not just inside printf

#!/usr/bin/env bash

BoxText(){
	# Enforce default border character as =
	Char=${2:-'='}
	CharMarginSize=2

	# Create Bar value
	printf -v Bar '%*s' $((${#1} + (CharMarginSize * 2) + 2)) ' '

	# Create CharMargin value
	printf -v CharMargin "$Char"'%.0s' $(seq 1 $CharMarginSize)

	# Print box with text
	printf '%s\n%s %s %s\n%s\n' "${Bar// /$Char}" "$CharMargin" "$1" "$CharMargin" "${Bar// /$Char}"
}

BoxText 'Example Title' '▧'
▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧
▧▧ Example Title ▧▧
▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧
1 Like

Scripting an alert for when a YouTuber is live

A script to check if a YouTuber is live. When given a YouTuber’s homepage as an argument it’ll output live, offline or an error description.

Inspired by an alternative script here

Is a YouTuber live?

sudo touch /usr/local/bin/ytcheckiflive
sudo chmod +x /usr/local/bin/ytcheckiflive
sudo nano /usr/local/bin/ytcheckiflive
# Paste the following and save:
#!/usr/bin/env bash

# Usage: ytcheckiflive <YouTuber's homepage URL>
# Example: ytcheckiflive https://www.youtube.com/channel/UCfp-lNJy4QkIGnaEE6NtDSg

Err(){
	printf 'ERROR: %s\n' "$2" 1>&2
	[ $1 -gt 0 ] && exit $1
}

# Check for curl dependency
[ -z $( which curl ) ] && Err 1 "'curl' not found."

# Get YouTuber's homepage
Data=$( curl -s "$1" )

# This string should be present if the URL is correct
MatchForGood='{"runs":[{"text":'

# This string should be present if the YouTuber is live
MatchForLive='{"iconType":"LIVE"}'

while read Line; do
	[ -z $IsGood ] && [[ $Line == *$MatchForGood* ]] && IsGood=1
	[ -z $IsLive ] && [[ $Line == *$MatchForLive* ]] && IsLive=1
	[ -n "$IsGood" ] && [ -n "$IsLive" ] && break;
done <<< "$Data"

[ -z "$IsGood" ] && Err 1 "Not a recognized channel"
if [ -n "$IsLive" ]; then
	printf 'live'
else
	printf 'offline'
fi

# See if they're live using their YouTube homepage:
ytcheckiflive https://www.youtube.com/channel/UCfp-lNJy4QkIGnaEE6NtDSg

Turning it into an alert

If the above script is called at 5 minute intervals with a list of YouTubers it can be used to produce an alert if one of them goes live.

Below is an example script intended to be added to a user’s crontab, it’ll use a temp file to enforce only one alert per “live” YouTuber session and uses notify-send to let the user know.

mkdir -p ~/.local/bin/
touch ~/.local/bin/ytalertiflive
chmod u+x ~/.local/bin/ytalertiflive
nano ~/.local/bin/ytalertiflive
# Paste the following and save:
#!/usr/bin/env sh

Err(){
	notify-send "ERROR: $2"
	[ $1 -gt 0 ] && exit $1
}

# Check for ytcheckiflive dependency
[ -z $( which ytcheckiflive ) ] && Err 1 "'ytcheckiflive' not found."

AlertIfLive(){
	Name=$1

	# Get the YouTuber's live status from ytcheckiflive (script above)
	Status=$( ytcheckiflive "$2" )
	ExitCode=$?

	[ $ExitCode -gt 0 ] && Err "$ExitCode" "$Status"

	if [ "$Status" = 'offline' ]; then

		# If applic. remove the live state so there's an alert when they're live next
		[ -f "$HOME/.cache/ytislive_$Name" ] && rm "$HOME/.cache/ytislive_$Name"

	elif [ "$Status" = 'live' ]; then
		if [ ! -f "$HOME/.cache/ytislive_$Name" ]; then

			# Set the live state so there's only one alert for this "live" session
			touch "$HOME/.cache/ytislive_$Name"

			# Alert the user
			notify-send -t 0 "YouTuber $Name is LIVE"

		fi
	else
		Err 1 "Incompatible status"
	fi
}

# List of YouTubers to check
# Syntax: AlertIfLive <Name of YouTuber> <Homepage of YouTuber>

# Example:
AlertIfLive TerminalForLife https://www.youtube.com/channel/UCfp-lNJy4QkIGnaEE6NtDSg
AlertIfLive DLN https://www.youtube.com/c/DestinationLinuxNetwork

Running the script every 5 minutes with chron

crontab -e # (don't use sudo)
# Append the following and save to apply changes...
# Don't forget to change USER_HERE to the name of the user
# Check for live YouTubers every 5 minutes
*/5 * * * * PATH='/home/USER_HERE/.local/bin:/usr/local/bin:/usr/bin:/bin'; /home/USER_HERE/.local/bin/ytalertiflive
3 Likes

Proof of concept: User defined fractional scaling TUI keyboard for touch devices in BASH

At the moment this is taking keys from a user defined array and requires symmetry but future incarnations will use a heredoc to allow a user to determine the shape, size and orientation of every key from simple text art of the desired keyboard. When it outputs fractional scaling and touch zoning will be included.

If combined with mouse-in-terminal this project grants touch support to TUI’s that need a keyboard or want to define their own.

As it’ll take a decent chunk of time to complete and I can’t think of an immediate usecase this’ll be on the far back burner but just wanted to show it can be done and i’ll cover some of the bits involved in future TTs.

bash-kbd

2 Likes

Speed testing

time, just a cool command I can’t believe I didn’t know…

# Try it:
# How long does it take to sleep for 2 seconds?
time sleep 2
real	0m2.002s
user	0m0.001s
sys		0m0.000s
# How long does it take to list every file/folder
# in your home directory recursively?
time ls -laR ~
real	0m4.440s
user	0m0.656s
sys		0m2.585s

Quoted summary of what each mean. For the fascinating deep dive click the link at the bottom.

  • Real is wall clock time - time from start to finish of the call. This is all elapsed time including time slices used by other processes and time the process spends blocked (for example if it is waiting for I/O to complete).
  • User is the amount of CPU time spent in user-mode code (outside the kernel) within the process. This is only actual CPU time used in executing the process. Other processes and time the process spends blocked do not count towards this figure.
  • Sys is the amount of CPU time spent in the kernel within the process. This means executing CPU time spent in system calls within the kernel, as opposed to library code, which is still running in user-space. Like ‘user’, this is only CPU time used by the process. See below for a brief description of kernel mode (also known as ‘supervisor’ mode) and the system call mechanism.

Quoted from: https://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1/556411#556411

1 Like

Would anyone like help or a better explanation on something?

Is there anything you’d really like me to take a look at or a way I might be able to “unstuck” you from a particular problem?

For reference i’m most useful in the Fedora and Debian universes (RHEL, Ubuntu, ect).

2 Likes

Ubuntu (probably most Debian-based Distros)

What are the ramifications of the message “XYZ has been set to manual install.” This occurs on the CLI when you accidentally attempt to install

  • sudo apt-get install XYZ

when it is apparently already installed (automatically or as part of the Distro)?

All of the packages are either manually installed or automatically installed as a dependency of another package. This is the flag that “apt autoremove” uses to remove packages no longer required because a top level package was uninstalled.

For example, I recently setup a system that didn’t require light-locker, so I uninstalled it, but since lightdm was marked as an automatic install, if I would have used “apt autoremove”, then it would have uninstalled lightdm. So to prevent that, I used the command “apt-mark manual lightdm” to mark the lightdm package as manually installed, there is also the opposite to change it from manual to automatically installed, “apt-mark auto lightdm”.

It sounds like “apt install” automatically marks a package as manual, if it is already installed, which makes sense to me.

4 Likes

@Ulfnic Ok. I’ve got one for you to look at. (If you have the time.)

There is a CLI app that I’ve grown very fond of: bible-kjv
It’s just a really fast way for me to search for passages containing a word, and I’ve used it for about two or three years now.

Sadly, after upgrading Pop to the latest release, it no longer functions. It runs fine if I type out a specific reference (which is handy enough) but it no longer searches for words.

Here’s the kicker. While it stopped working under Pop_21.04 (kernal5.13.0-7614-generic & bash5.1.4) It works fine under Mint 20.1. (kernal5.4.0-84-generic & bash 5.0.17)

Would my BASH version be messing with how this program receives variables?

*edit* just noticed @LinuxUser answer this, good one!

<package> has been set to manual install

You’ll get this message if you tell apt to install a package that’s already been installed as a dependency. It tells apt that you want to keep it even if the package(s) using it are removed.

Example

This will install newsboat and it’s dependency libstfl0, then remove both…

sudo apt install newsboat
sudo apt remove newsboat

This will remove just newsboat…

sudo apt install newsboat
sudo apt install libstfl0
sudo apt remove newsboat
1 Like

The closest I could get was this:

“If a manually-installed driver is being used, so that it is not possible to switch to any of the other drivers for that device, the radio buttons and labels for those other drivers should be insensitive.”

SoftwareAndUpdatesSettings - Ubuntu Wiki

“insensitive” probably means greyed out and i’m guessing the “manually-installed driver” option is just the one Ubuntu provides.

1 Like

I’m downloading Pop_21.04 to see if I can replicate the issue.

I’m not sure if i’ll be able to catch the “why”, it sounds like a dependency version problem but it might not be.

The quickie answer is getting it in a portable format but I don’t see it available as an AppImage/Flatpak/Snap. Making it an AppImage from a working distro is probably the way to go.

1 Like

I tried it on Ubuntu Mate 21.04 and it fails there, too. Sure enough, Bash is sitting at 5.1.

In digging around a bit, I stumbled across the sourcecode at GitHub - umeboshi2/bible-kjv: Fork of https://git.dgit.debian.org/bible-kjv.git for learning emscripten

This looks to be the correct package. I just wish I knew more about coding to better understand what I’m looking at. Some of this is in C and some of this is bash script.

For example, the compiled command to call up a reference range is found in the bible-kjv/bible.c at master · umeboshi2/bible-kjv · GitHub code. (Looks like line# 223-253. But I don’t see how BASH versions would make a difference to compiled code.

Wondering if I need to re-compile this from scratch on Pop to see if has to compile differently under the new bash??

Looks like someone did a rewrite in perl, GitHub - mmlj4/perl_brs_rewrite: A Perl rewrite of the original Bible Retrieval System

2 Likes

Wow! This is fantastic! I wanted to attempt something similar in Python, but had very little clue how to do so.

Large Edit and Redaction

I see what you are saying now.

If I type bible ??perfume I get

  Searching for 'perfume'... [3 refs]
  References [3]:  Ex30:36 Ex30:38 Pr27:10

Which is weird because Exodus 30:37 is where “perfume” is.

bible Ex30:37

Exodus 30

  37 And as for the perfume which thou shalt make, ye shall not make to
yourselves according to the composition thereof: it shall be unto thee holy for
the LORD.

Now I’m really not sure if this is working or not.

1 Like

When I attempt to search from Pop, I get 0 results… and I am under the distinct impression it is not even trying to search.

Yet, it works in Mint.

Which version of bash are you running, just curious?

I was able to replicate your problem in PopOS 21.04

Perhaps worth a mention, this line is at the top of the bible.c :slight_smile:

Status:       Experimental (Do Not Distribute)

I gave compiling it a go in Debian/Ubuntu/Pop 21 and got this error:

bible.c:170:10: fatal error: editline.h: No such file or directory
 #include <editline.h>
          ^~~~~~~~~~~~

On Debian and Ubuntu this is in the libeditline-dev package and installing it allowed me to proceed with compiling.

PopOS 21.04 doesn’t have libeditline-dev in it’s repos. It does have libeditreadline-dev (which appears to be a replacement?) but that didn’t fix the compiling issue. Taking a wild guess Pop 21 probably has some fundamental library conflicts with bible-kjv.

1 Like

Wow! You went all out on this. Thank you for digging so deep.

Did the Debian/Ubuntu compile run as normal? (allowing searches via the ?? command?)

Yes, both Debian and Ubuntu were able to search normally for me with bible-kjv where PoP 21 is blank. If you don’t mind waiting a few posts i’ll solve your issue directly but it’s looking like an AppImage is going to be the way to go.

2 Likes

Chasing dependency rabbits /(・ × ・)\

in the Debian universe.

Whether it’s for troubleshooting, lightening your dependency load or tracking down what you need to make an AppImage sometimes you just need to chase those dependency rabbits.

# Check the dependencies of a package (whether installed or not).
apt depends bash
# Output:
bash
  PreDepends: libc6 (>= 2.15)
  PreDepends: libtinfo6 (>= 6)
  Depends: base-files (>= 2.1.12)
  Depends: debianutils (>= 2.15)
  Conflicts: bash-completion (<< 20060301-0)
  Recommends: bash-completion (>= 20060301-0)
  Suggests: bash-doc
  Replaces: bash-completion (<< 20060301-0)
  Replaces: bash-doc (<= 2.05-1)

There’s currently 9 different dependency categories called “Package Interrelationship Fields” and a few more for compiling.

This is my TLDR from the Debian Docs

Dependency Type Means
Pre-Depends I must have this and if it’s not there install it before Depends
Depends I must have this
Recommends I really should have this but can do without it
Suggests This enhances me (soft Recommends)
Breaks I’ll break this if you install me
Conflicts Unless i’m removing or replacing this, you can’t install me (see: Replaces)
Provides I’ll provide this other package virtually
Replaces I’ll remove or replace this
Enhances I’ll enhance this (reverse of Suggests)

So what is apt depends <package> saying about the dependencies of bash?

  • It must have libc6, libtinfo6, base-files and debianutils. If any of these are missing the first two should be installed before the last two.
  • bash-doc would be an enhancement
  • It really should have bash-completion >= version 20060301-0 installed too.
  • It’ll remove bash-completion and bash-doc if their versions are too old.

It’s never just one rabbit. /(・ × ・))))))))\

apt depends gives the immediate dependencies, not the dependency tree (all dependencies of all dependencies). Getting the sum total of packages required for a package to work requires using apt depends recursively on every required package till there’s no more requirements.

There’s a package apt-rdepends that claims to do just that.

sudo apt install apt-rdepends
apt-rdepends bash
# Output:
bash
  Depends: base-files (>= 2.1.12)
  Depends: debianutils (>= 2.15)
  PreDepends: libc6 (>= 2.15)
  PreDepends: libtinfo6 (>= 6)
base-files
  PreDepends: awk
awk
debianutils
  PreDepends: libc6 (>= 2.15)
libc6
  Depends: libgcc1
libgcc1
  Depends: gcc-8-base (= 8.3.0-6)
  Depends: libc6 (>= 2.14)
gcc-8-base
libtinfo6
  Depends: libc6 (>= 2.16)

It can also output in dot readable format (part of graphviz) that can render all kinds of relationship graphs.

sudo apt install graphviz
apt-rdepends --dotty bash | dot -Tpng > bash-dependencies.png
# Open bash-dependencies.png

graph
Box: Package
Circle: Meta package
Yellow arrow: PreDepends
White arrow: Depends
(colors were inverted in krita)

Chasing meta rabbits /(・ × ・))))))) × ・)>)))))))))))))))))\

The problem with apt-rdepends is it stops at the meta package level without recursing into it’s dependencies (see: awk above) so a lot of package rabbits are missing.

Thankfully apt-cache has CLI options that’ll recurse into all PreDepends and Depends packages similar to apt-rdepends but including the meta packages.

# 8 dependencies
apt-rdepends bash | grep "^[^ ]"
# __vs__
# 25 dependencies
apt-cache depends --recurse --important bash | grep "^[^ <]"

Worth mentioning… while this doesn’t appear to affect my grep above, apt-cache seems to have a bug where this random pipe can show up:

apt-cache depends --recurse --important bash
...
readline-common
 |Depends: dpkg
  Depends: install-info
...

There’s also no option for dot graph output which is a lifestlye to which we’ve become acustom.

Visualizing the full dependency rabbit hole

Time to write some BASH…

This script converts the output of apt-cache depends --recurse --important <package> into a dot graph with an output the same as apt-rdepends --dotty but including meta package dependencies:

sudo apt install graphviz
Path="$HOME/.local/bin/deptodot"
mkdir -p "${Path%/*}"; touch "$Path"; chmod u+x "$Path"; nano "$Path";
# Paste the following:
#!/usr/bin/env bash
Err(){
	printf '%s\n' "$2" 1>&2
	[ $1 -gt 0 ] && exit $1
}

PackageName=$1
[ -z "$1" ] && Err 1 'No package provided'
[ -z "$( apt-cache search --names-only "^$1$" )" ] && Err 1 "Package doesn't exist"

printf '%s\n' 'digraph packages {'
printf '%s\n' 'concentrate=true;'

while IFS= read -r Line; do
	set -- $Line

	# If the line doesn't start with a space and doesn't have a :
	# it's a package, otherwise it's a package dependency
	if [ "${Line:0:1}" != ' ' ] && [ "${Line##*:}" != '$Line' ]; then
		CurrentPackage=$Line
		CurrentSubPackage=

		# If it starts with a < it's a meta package
		if [ "${Line:0:1}" = '<' ]; then
			printf '%s\n' "\"$CurrentPackage\" [shape=ellipse];"
		else
			printf '%s\n' "\"$CurrentPackage\" [shape=box];"
		fi
	else

		# If the dependency starts with a < it's a meta package and will
		# uniquely act as the parent to dependencies on following lines.
		[ "${2:0:1}" = '<' ] && CurrentSubPackage=$2

		if [ "${Line##*PreDepends:}" != "$Line" ]; then

			# Pre-dependency of the current package
			printf '%s\n' "\"$CurrentPackage\" -> \"$2\"[color=blue];"
		elif [ "${Line##*Depends:}" != "$Line" ]; then

			# Dependency of the current package
			printf '%s\n' "\"$CurrentPackage\" -> \"$2\";"
		else
			# Sub-dependency of the current meta dependency of the current package
			printf '%s\n' "\"$CurrentSubPackage\" -> \"$1\";"
		fi
	fi
done <<< $( apt-cache depends --recurse --important "$PackageName" )
# If you don't have $HOME/.local/bin in your path, run
# this and consider adding it to your ~/.bashrc
PATH="$HOME/.local/bin:$PATH"

deptodot 'bash' | dot -Tpng > bash-dependencies.png
# Open bash-dependencies.png

Full BASH dependency tree (click for no blur)

Takeaway

# List immediate dependencies
apt depends <Package>

# List full dependency tree
apt-cache depends --recurse --important <Package> # CLI
deptodot <Package> | dot -Tpng > image.png # Graphically using graphviz (script above)

# List full dependency tree except meta package dependencies
apt-rdepends <Package> # CLI
apt-rdepends --dotty <Package> | dot -Tpng > image.png # Graphically using graphviz
5 Likes