Since I have changed to Linux, I had more opportunities to create shell scripts. The scripts that I would like to share will end up here. These shell scripts don’t belong to any particular projects, so I don’t upload them to GitHub, but I also provide it to you so you can make use of it, or learn from it.
⚠️ CAUTION! ⚠️
As a general rule of thumb, DO NOT run code from the internet on your PC that you don’t understand. If you don’t understand these codes, consult with a trusted IT professional first, or refrain from executing such codes.
I do not hold any responsibility to any harm these codes may cause in your system. I also do not hold any responsibility for any involved 3rd party code the snippets may execute.
This notice is not an indication nor an admission of the following snippets containing malicious code. These snippets however could contain code, that can harm your system, if you do not proceed with proper caution.
Proton Product Updaters
Proton is a family of products, including email, vpn, cloud storage and password manager. Some of these product have supporting applications for Windows, Mac and Linux too, like Proton Mail Bridge, Proton VPN, etc. However, only Proton VPN introduced automatic updates in to the Linux ecosystem, by making the package also include an online repository link. Unfortunately, this leaves Proton Pass, Proton Mail Bridge, and Proton Mail (currently in beta, as of writing this page) without an automated update available, like Proton VPN does. To salvage this, I have wrote scripts to download the latest version and install them, which I can call whenever the application informs me, that there is a new version available. All of them are written with Ubuntu in mind, so the script only installs Debian packages. I would like to expand this to also install RedHat packages, by detecting the distro it is being executed on.
⚠️ WARNING! ⚠️
The update methods do not do any safety checks, whether the downloaded packages are altered or not. I only recommend to execute these scripts, if you can make sure, that the packages that are being downloaded are not intercepted, like with a VPN connection.
Proton Pass Update
Proton Pass is the password manager of the family. Proton Pass’ package name is the most consistent in naming the latest package, so it is not a huge deal to update: just download, and install the package.
As the earliest script I wrote since I’ve switched to Linux, this script uses the dpkg
command, because I still didn’t knew that apt
could also install .deb
files. I kept the script using the dpkg
package manager.
XDG_DOWNLOAD_DIR=$(xdg_user_dir DOWNLOAD) wget https://proton.me/download/PassDesktop/linux/x64/ProtonPass.deb -O $XDG_DOWNLOAD_DIR/ProtonPass.deb sudo dpkg -i $XDG_DOWNLOAD_DIR/ProtonPass.deb
Proton Mail (Beta) Update
Proton Mail is the latest application in the family, and it is still in beta as of writing this page. The package name contains the keyword beta, so once it is released, it will needs to be updated.
XDG_DOWNLOAD_DIR=$(xdg_user_dir DOWNLOAD) wget https://proton.me/download/mail/linux/ProtonMail-desktop-beta.deb -O $XDG_DOWNLOAD_DIR/ProtonMail-desktop-beta.deb sudo apt install $XDG_DOWNLOAD_DIR/ProtonMail-desktop-beta.deb
Proton Mail Bridge Update
Proton Mail Bridge is the worst from automation viewpoint. The package name contains the version number, so it cannot simply be automated only by referring to the lates package file’s URL, because it will not updated. I have, however, found a JSON file for applications to fetch updates, it is not public, so one must fish for it. How did I get hold of the URL for the JSON file? Well, that’s a trade secret. 😉 (This bird told me 👉 🐦)
I came across the jq
command that could parse the JSON response, while debugging a bug, but I’ve opted to use grep
only, so it stays light on what tools you need to have installed on your system. Otherwise, the JSON file is easy enough, by simply containing the link to the correct Debian package. And since it also have a brief changes notes, I thought I might as well show that to the user too.
current_version_linux="$(wget -qO- https://proton.me/download/current_version_linux.json)" echo -n "Latest version: " echo $current_version_linux | grep -oE '"Version": "([0-9]+\.){2}[0-9]"' | grep -oE '([0-9]+\.){2}[0-9]' echo $current_version_linux | grep -Pzo '"ReleaseNotes": "[^"]*",' | grep -Pzo '"[^"]*",' | grep -Pzo '[^"]*' 2>&1; echo XDG_DOWNLOAD_DIR=$(xdg-user-dir DOWNLOAD) wget -O $XDG_DOWNLOAD_DIR/protonmail-bridge_amd64.deb $(echo $current_version_linux | grep -oE '"DebFile": "[^"]*",' | grep -oE '"[^"]*",$' | grep -oE '"[^"]*"' | grep -oE '[^"]*') sudo apt install $XDG_DOWNLOAD_DIR/protonmail-bridge_amd64.deb
Test Network Access
I manage a network, and also provide support to it’s users, when someone doesn’t have access to the internet. I rather keep who or where private. For that purpose, I’ve wrote a network testing script, both for Windows and Linux. However, these scripts are specific to that network topology, so they are not very useful to share. (They would immediately tell you, that the network is down in every case, and very unlikely to ever work.) However, with the recent completion of the Linux Admin course, I’ve realized, that I can make the script dynamic enough, to be adaptable to any network configuration—Linux only for now.
The script below requires nothing at all, just to run it as-is. The script currently only works with wired network, and assumes that your client only have one (cable) network adapter (i.e. enp#s#
). The testing mostly happens by pinging. It pings 4 times, and with 0.02 s intervals. It does the following steps, and if any of them fails, it gives you a report, and suggested steps to resolve it.
- It first checks for the first cable network device.
- Then it reads the default gateway IP, and pings it.
- Reads your currently used DNS IP, and pings it.
- Pings Google’s default DNS IP.
- Tries to resolve <www.google.com>.
#!/bin/sh testhost() { echo -n "Reaching $1 . . . " #if ping -c4 -i.02 $2 >/dev/null; then if ping -c1 $2 >/dev/null; then echo reached else echo "failed" >&2 echo "there is an issue $3." exit 1 fi } ensp=$(ifconfig | grep -Eo 'enp[0-9]+s[0-9]+') gw=$(route -n | grep "$ensp" | grep G | head -n1 | awk '{ printf $2 }') ispdns=$(resolvectl status | grep 'DNS Server:' | awk '{ printf $4 }') testhost Gateway $gw 'between your PC and the gateway' testhost 'ISP DNS' $ispdns 'between the gateway and the ISP DNS server' testhost 'Google DNS' 8.8.8.8 'somewhere at the ISP' echo You have internet connection, and can reach other hosts. tmpfile=$(mktemp) dig -t ns google.com >$tmpfile case $? in 0) if grep -Eq '^google.com. *[0-9]* *IN *NS' $tmpfile; then echo Your PC can resolve host names. You should be able to surf the web with no issues. echo If you have failed to reach a host by name, it may be down? Try to reach it again. else echo The DNS request of google.com did not yielded the expected results. echo This means, that name resolution does not work. Contact your ISP. echo Alternatively this could be an issue at Google\'s servers. fi ;; 1) echo "Could not test name resolution: The script writer messed up the arguments, or the \`dig' command changed since the script was written." echo "This is not any indication of the status of your network. You should just get the latest version of the script, or report it to the writer." ;; 9) echo "The configured DNS server doesn't reply. Restarting the router if you have one," echo "or disconnecting and reconnecting can resolve such network errors, and is the least involved solution." ;; *) echo The issue is not recognized. echo While you have a working internet connection, host name resolution is not working. echo This may be due to a configuration issue. ;; esac rm -f $tmpfile
Audio Funnel
It was madness to create this script. Here’s what it does: It creates two virtual output devices—“🔇 Silent Audio Share” and “🔊 Audio Share”—that you can direct your application’s audio to in Pulse Audio, and a single virtual input device—“📢 Shared Audio”—which you can set as an input source for any meeting application, that receives the audio from the virtual output device, alongside of the input from your chosen microphone, or whichever input device you’re using. The difference between the two virtual output device, is that “🔊 Audio Share” also mirrors the audio to the audio device of your choice, like your default speakers.
When called with the ’on’ argument, it then offers you to first set the output device you want “🔊 Audio Share” to mirror the audio from the offered list, then selecting the input device to mirror in the “📢 Shared Audio” virtual input device, along with the shared application audio. You can call the script with the ’on’ argument repeatedly, to change the devices. Call it with the ’off’ argument, to remove the devices.
The reason I needed this, is because I had to share my screen during a meeting, and only then I’ve found out, that none of the application can share the audio under Wayland. But I was well aware, that you can, luckily, program Pulse Audio. All I had to do, is figure out how.
I have found two very helpful answers on StackExchange that helped me construct the final code. The link to those answers are in the code. The code is free for personal use.
# source: # - https://unix.stackexchange.com/a/594698/18071 # - https://unix.stackexchange.com/a/620178/18071 basename=share_audio SSA_name=$basename.silent ASA_name=$basename.audible SAS_name=$basename.source SSA_desc='🔇 Silent Audio Share' ASA_desc='🔊 Audio Share' SAS_desc='📢 Shared Audio' if [[ -z "$1" ]]; then echo "Usage: $0 [on|off]" echo "Creates two audio sinks and an audio source to make it possible for you to share and record audio from an application or the system." echo "on -- creates the sinks and audiosource." echo "off -- removes the sinks and audiosource." echo echo "The created audio modules:" echo "$SAS_desc" echo " The audio source that receives both your chosen audio source's output, and all audio directed to one of the sinks" echo "$SSA_desc" echo " The audio sinks that redicerts all received audio to the $SAS_desc source." echo "$ASA_desc" echo " Same as $SSA_desc sink, but it also redirects the received audio to your chosen audio sink as well." echo echo "Currently created and managed audio modules by this script [technical]:" pactl list modules short | grep $basename elif [[ "${1^^}" = "ON" ]]; then echo 'Select a sink to output the shared audio when you want to hear it:' select sink in $(pactl list sinks short | awk '{ print $2 }'); do target_sink=$sink [[ -z "$sink" ]] && exit || break done echo 'Select a source that you want to use while you are sharing audio:' select source in $(pactl list sources short | awk '{ print $2 }'); do target_source=$source [[ -z "$source" ]] && exit || break done if [[ "$(pactl list modules short | grep "\\bsink=$SSA_name\\b" | grep -o 'source=\S*')" != "source=$target_source" ]]; then pactl list modules short | grep -q "\\bsink=$SSA_name\\b" && pactl unload-module $(pactl list modules short | grep "\\bsink=$SSA_name\\b" | awk '{ print $1 }') fi if [[ "$(pactl list modules short | grep "\\bsink_name=$ASA_name\\b" | grep -o 'slaves=\S*' | grep -o ',[^,]*$')" != ",$target_sink" ]]; then pactl list sinks short | grep -q "\\b$ASA_name\\b" && pactl unload-module $(pactl list modules short | grep "\\bsink_name=$ASA_name\\b" | awk '{ print $1 }') fi pactl list sinks short | grep -q "\\b$SSA_name\\b" || pactl load-module module-null-sink sink_name=$SSA_name sink_properties="'device.description=\"$SSA_desc\"'" pactl list sources short | grep -q "\\b$SAS_name\\b" || pactl load-module module-remap-source source_name=$SAS_name source_properties="'device.description=\"$SAS_desc\"'" master=$SSA_name.monitor pactl list sinks short | grep -q "\\b$ASA_name\\b" || pactl load-module module-combine-sink sink_name=$ASA_name sink_properties="'device.description=\"$ASA_desc\"'" slaves=$SSA_name,$target_sink pactl list modules short | grep -q "\\bsink=$SSA_name\\b" || pactl load-module module-loopback source=$target_source sink=$SSA_name elif [[ "${1^^}" = "OFF" ]]; then pactl list modules short | grep -q "\\bsink=$SSA_name\\b" && pactl unload-module $(pactl list modules short | grep "\\bsink=$SSA_name\\b" | awk '{ print $1 }') pactl list sinks short | grep -q "\\b$ASA_name\\b" && pactl unload-module $(pactl list modules short | grep "\\bsink_name=$ASA_name\\b" | awk '{ print $1 }') pactl list sources short | grep -q "\\b$SAS_name\\b" && pactl unload-module $(pactl list modules short | grep "\\bsource_name=$SAS_name\\b" | awk '{ print $1 }') pactl list sinks short | grep -q "\\b$SSA_name\\b" && pactl unload-module $(pactl list modules short | grep "\\bsink_name=$SSA_name\\b" | awk '{ print $1 }') fi
Snap Firefox User-Font Fix
While I was configuring http://www.jadaml.website/ website, I came across an issue, where Firefox on my PC doesn’t select the font, I have chosen for this page, which would be the Libertinus font family. While I want to talk about distinct features, everything differs in the 3 Firefox installation:
- The problematic Firefox installation is installed on a Kubuntu from Snap,
- the other is installed on an immutable Arch Linux variant from Flathub—this OS uses KDE too,
- and the last one is running on my Android phone.
While too many things are different from each other, one of the possible issue I’ve found is with Snap, where the user installed fonts aren’t detected properly. There is also a bug report regarding this on both Bugzilla and Ubuntu’s bug reporting system, none of them seem to be resolved at the moment. The relevance here, as a script, is that ytxmobile commented to the Firefox Snap bug at Bugzilla a possible workaround. The commands are simple enough, if that you read the manual page for the ln
command11 (man page from Ubuntu online), which will end up looking like this:
ln -s ~/.local/share/fonts ~/snap/firefox/current/.local/share/fonts ln -s ~/.config/fontconfig ~/snap/firefox/current/.config/fontconfig
After this, to take effect, you need to restart Firefox. Once restarted, the user fonts should appear correctly.
This post is also shared on my workshop site, and will be expanded with new scripts as time goes on:
The scripts that I would like to share will end up here.
- The egl-wayland 1.1.15 KDE package which is shipped with Ubuntu have a bug under Wayland with nVidia drivers, which crashes all applications that use the web browser control, which also effects KDE’s Help Center with which this link would open on KDE. The 1.1.16 version fixes this, but it is only packaged with Ubuntu 15.04, which is still in development. In the meantime, try to use
yelp man:ln
or use the terminal to read the man page ofln
command:man 1 ln
. ↩︎