External USB Refuses to Mount on macOS

I was working on some cryptography homework and remembered that I wanted to plug my iPhone into my iMac to charge it up.  I have a USB dongle that gives me room for 4 more ports.  I have noticed that the dongle is rather pathetic and can’t handle much throughput or utilization.  It doesn’t have enough for all of my peripherals so I try to use it for light-weight things like a wireless receiver for my Microsoft mouse and other stuff.  Well, this time I had a USB drive plugged in in addition to my iPhone, wireless sensor, and something else.  I opened Finder and checked my iPhone and noticed that it hadn’t been backed up for over two months.  I started an encrypted backup of my phone.  A few moments later, my external hard drive unmounted on its own.  I think the dongle basically stopped communicating with it because of the communication requirements of the iPhone backup.  I decided to move that USB drive to a port that is on my iMac directly.  When I plugged it in, after roughly two or three minutes, I recieved the error that the disk could not be mounted.

Could not mount DiskManagement disenter error 0
Could not mount DiskManagement disenter error 0

I tried to mount it by hand from the command line and got “Resource busy”.  Interesting.  What’s using it?  I’m not.

What disks are attached?

$ diskutil list
/dev/disk0 (internal, physical):
#:                     TYPE NAME                SIZE     IDENTIFIER
0:    GUID_partition_scheme                    *3.0 TB   disk0
1:                      EFI EFI                 209.7 MB disk0s1
2:               Apple_APFS Container disk2     3.0 TB   disk0s2

/dev/disk1 (internal, physical):
#:                     TYPE NAME                SIZE     IDENTIFIER
0:    GUID_partition_scheme                    *121.3 GB disk1
1:                      EFI EFI                 209.7 MB disk1s1
2:               Apple_APFS Container disk2     121.1 GB disk1s2

/dev/disk2 (synthesized):
#:                     TYPE NAME                SIZE     IDENTIFIER
0:    APFS Container Scheme -                  +3.1 TB   disk2
                            Physical Stores disk1s2, disk0s2
1:              APFS Volume Macintosh HD - Data 728.4 GB disk2s1
2:              APFS Volume Preboot             81.8 MB  disk2s2
3:              APFS Volume Recovery            526.6 MB disk2s3
4:              APFS Volume VM                  9.7 GB   disk2s4
5:              APFS Volume Macintosh HD        11.0 GB  disk2s5

/dev/disk3 (external, physical):
#:                     TYPE NAME                SIZE     IDENTIFIER
0:    GUID_partition_scheme                    *2.0 TB   disk3
1:                      EFI EFI                 209.7 MB disk3s1
2:     Microsoft Basic Data PJADATA             2.0 TB   disk3s2

Let’s do a generalized search for “disk” because a) I know /dev/disk3s2 isn’t mounted; and b) if something is using that device, it is using the device itself, not a disk as a mount:

$ sudo lsof | grep disk
UserEvent 155 root txt REG 1,7 29360 1152921500312401745 /System/Library/UserEventPlugins/com.apple.diskarbitration.plugin/Contents/MacOS/com.apple.diskarbitration
diskarbit 184 root cwd DIR 1,7 704 2 /
diskarbit 184 root txt REG 1,7 161760 1152921500312399232 /usr/libexec/diskarbitrationd
diskarbit 184 root txt REG 1,7 28056 12987595370 /Library/Preferences/Logging/.plist-cache.8jY7AXDN
diskarbit 184 root txt REG 1,7 28504528 1152921500312401357 /usr/share/icu/icudt64l.dat
diskarbit 184 root txt REG 1,7 1558736 1152921500312400397 /usr/lib/dyld
diskarbit 184 root 0r CHR 3,2 0t0 319 /dev/null
diskarbit 184 root 1u CHR 3,2 0t0 319 /dev/null
diskarbit 184 root 2u CHR 3,2 0t846 319 /dev/null
diskmanag 388 root cwd DIR 1,7 704 2 /
diskmanag 388 root txt REG 1,7 2190864 1152921500312399722 /usr/libexec/diskmanagementd
diskmanag 388 root txt REG 1,7 28056 12987595370 /Library/Preferences/Logging/.plist-cache.8jY7AXDN
diskmanag 388 root txt REG 1,7 1558736 1152921500312400397 /usr/lib/dyld
diskmanag 388 root 0r CHR 3,2 0t0 319 /dev/null
diskmanag 388 root 1u CHR 3,2 0t0 319 /dev/null
diskmanag 388 root 2u CHR 3,2 0t0 319 /dev/null
UserEvent 511 pja txt REG 1,7 29360 1152921500312401745 /System/Library/UserEventPlugins/com.apple.diskarbitration.plugin/Contents/MacOS/com.apple.diskarbitration
fsck_exfa 50042 root 3u CHR 1,13 0t0 2223 /dev/rdisk3s2

Ah! fsck is running!  The disk filesystem is being checked.  Now, let’s narrow our search just out of curiosity of what else fsck is doing:

$ sudo lsof | grep fsck
fsck_exfa 50042 root cwd DIR 1,7 704 2 /
fsck_exfa 50042 root txt REG 1,7 90400 1152921500312404229 /System/Library/Filesystems/exfat.fs/Contents/Resources/fsck_exfat
fsck_exfa 50042 root txt REG 1,7 1558736 1152921500312400397 /usr/lib/dyld
fsck_exfa 50042 root 0u CHR 3,2 0t180 319 /dev/null
fsck_exfa 50042 root 1u CHR 3,2 0t180 319 /dev/null
fsck_exfa 50042 root 2u CHR 3,2 0t180 319 /dev/null
fsck_exfa 50042 root 3u CHR 1,13 0t0 2223 /dev/rdisk3s2

When it was finished, my disk mounted all by itself.

$ mount |grep disk3
/dev/disk3s2 on /Volumes/PJADATA (exfat, local, nodev, nosuid, noowners)

Taking Inventory of Your Hardware

CPUs

How many does your system have?
Linux
# lscpu | egrep 'CPU\(s\)'
CPU(s): 32
On-line CPU(s) list: 0-31
NUMA node0 CPU(s): 0-7,16-23
NUMA node1 CPU(s): 8-15,24-31

Python Snippets

File handling

You can use a file handle or you can work with file objects using with.  Using with has the benefits of easier-to-read syntax and cleaner exception handling.  Any files that are open will automatically be closed after your done when you use with.

Checking if a File Exists
from pathlib import Path

my_file = Path("/path/to/file")
if my_file.is_file():
    # file exists
Looping Over File Object
myfile = "output.txt"

# file handle
file = open(myfile, "r")         # a=append, r=read, w=write, rw=read/write
for line in file: 
    print(line)

# file object
with open(myfile) as file:
    # read file line by line and output to list
    data = file.readlines()
    for line in data:
        print(line)
Writing To a File
myfile = "output.txt"

# file handle
file = open(myfile, "w")
file.write("Write these lines to your file") 
file.write("And close it when you're done")
file.close()

# file object
with open(myfile) as file:
    file.write("Write these lines to your file")
    file.write("And close it when you're done")
Writing to a File, Option 2
import sys

print("Send this error to stderr", file = sys.stderr)

f_handle = open('/tmp/output_20200511.txt', 'w')
print("Output to file", file = f_handle)
f_handle.close()

Types

Dustin Ingram – Static Typing in Python


VirtualEnv

When trying to run make htmldocs from kernel source, it recommends doing so from a Python virtualenv.  It’s a perfect time to document setting one up (per their recommendation).

sudo yum install -y ImageMagick graphviz python-virtualenv
virtualenv sphinx_1.4
. sphinx_1.4/bin/activate
pip install -r Documentation/sphinx/requirements.txt

XML Parsing

xml.dom.minidom

 

System Administration Links

chroot

An excellent guide from Bart Simons

Classics

hunter2

Support and Help

Password Recovery

[CentOS] Reset Root Password (single user mode)

If you don’t remember the root password, or you just want to boot in the most minimal way, you can add init=/bin/bash to your linux line to bypass the regular init sequence and just drop to a shell. The line should look like

linux /vmlinuz-xxx root=xxx ro init=/bin/bash

Whatever is after vmlinuz and root (where the xxxs are) leave as-is.  You can remove everything else on that line except for the leading word linux, vmlinuz, root and ro.  Add init=/bin/bash

The system ought to boot and present a bash shell running as user root.  If your system has full-disk encryption, you will need to enter the encryption password.

At this command line, run the following commands:

mount -o remount,rw /
mount /proc

Then you can view and modify the user database. The main user database file is /etc/passwd. It contains user names (for both physical users and system accounts), but passwords are in a different file /etc/shadow. If you’ve forgotten a password, you can’t recover it, all you can do is change it.

If you want to change the password for an account, run

passwd root

to change user root’s password.

If you see a token manipulation error then make sure you booted into an environment that supports read/write (use rw in linux line or remount as rw).

Once you’re finished, you can reboot.

umount /
reboot

[CentOS] Reset Root Password (live CD chroot mode)

  1. Boot into your live environment
  2. Make directory on which to mount your partition
    mkdir ./mnt || /bin/true
  3. Identify your disks using fdisk
    fdisk -l
  4. Look for the bootable partition of type Linux
  5. If your partition is a standard Linux partition:
    mount /dev/sda1 ./mnt
  6. If your partition is a Linux LVM:
    pvscan
    vgscan
    vgchange -a y
    lvscan
    mount /dev/sda1 ./mnt
    mount --bind /dev ./mnt/dev<
    mount --bind /proc ./mnt/proc
    mount --bind /sys ./mnt/sys
    
  7. Change root into your mounted directory
    chroot ./mnt /bin/bash
  8. Type passwd and enter a new password
  9. Type reboot to reboot your system
Provisioning

Foreman – PXEless Bare Metal & Virtual Machine Provisioning

Volume Management

[LVM] Working With LVM Volumes

 

Working From Home

I was perusing StackOverflow and came across a post containing tips and suggestions for working from home from experienced remote workers.  As I was reading through it, I remembered that I had a long run at working from home myself.  I once worked with a partner as consultants and we did this without an office.  He was usually on the road and I was usually rolling out of bed and planting myself in front of my computer.

IMPORTANT: I’m not an occupational therapist or a trained professional, this is only my opinion based on my experience.

Following their lead, I’ll start with location.

Location

I recently got a door on my office and it has made all the difference in the world.  I am sure my wife doesn’t fully appreciate them because I can lock them from the inside, but for me and my concentration, they have been key.  To build on what others are saying in the article, I do agree that having a different or isolated space is important.  If you have to work at the kitchen table in a room with constant distractions, more power to you.  Some people can, I’m not here to say it doesn’t work for you, but it doesn’t work for me.

In addition to a space, have proper lighting (I prefer natural light to artificial), room to spread out, a comfy chair, and a reliable headset for calls and to block out background noise.

Schedule

Your daily schedule should mimic what you would do if you were in the office or if your employer had business or operating hours.  If being home automatically signs you up for a collection of responsibilities and you can’t hold normal business hours at home, then make arrangements with your manager so that they are aware what hours will be your most productive and which hours will probably be chopped full of kid duties or whatever may block you from being productive.  Kids are a common cause for interrupted time but there are other things that may split up your day.  During your productive time, mentally prepare to focus on your work – get your tasks done and move on!

Daily Routine

Having food on hand prevents you from having to spend too much time trying to figure out what you’re doing for lunch.  Your home office may still be located in an area where there are lunch opportunities outside of the home that are faster than your at-home options.  Again, the decision is yours.  I recommend coming up with a solution that works 80% of the time by default – when you don’t have time to think about it, 80% of the time you can rely on a small list of options to meet your needs.  You don’t want to be opening the fridge every day asking yourself, “What do I feel like eating today?”  Personally, I’m pretty bad at answering that question myself so PB&J or cold-cuts will solve that issue 50% of the time.  I also know that leftovers will answer the question 40% of the time.  The other 10% of the time, I’ll take the opportunity to go for a walk and grab a burger at the corner.  I have 30+ restaurants within a 1-mile radius of my home office so I have options!  You may or may not.

The Great Outdoors

It’s hard to remember to get up and get out and do something so set a timer.  Buy an eff timer or a piece of software that will remind you to get up!  It’s important.  Don’t ignore our great outdoors!

Communication

Keep it simple.  I don’t like having 6 or 8 different channels for the 3 or 4 teams or groups with whom I have to communicate.  Use a chat solution that will notify you if you are mentioned if the noise gets to be too much but having to check a half dozen channels every so often is, in my opinion, is more disruptive than just having three or four large channels.  Once you decide on a solution, don’t hesitate to over communicate.  Not every conversation needs a fly gif from gliffy – just state your purpose and move on.  Not every comment requires a comment in return – just give it a thumbs up and move on.  You could spend all day in Teams adding fancy responses and trying to make everybody laugh every time something is said but that gets old after a while and isn’t sustainable.  Plus, it begs the question, “Do you have enough to do?”

Tools of the Remote Trade

Software selections have to be decided by, a.) your employer, or b.) your group.  The selection of software that may or may not work for you is beyond the scope of this post.  However, I will say that I do think that you need to invest in the following:

  1. a desk or desk-like surface – you’re going to regret sitting on your couch day in and day out
  2. proper lighting – staying in the dark is fine but eventually, you’ll want to reduce the contrast between your surroundings and your monitor – for your eyes’ sake
  3. proper chair – if it’s too hard, get a cushion; if it’s too soft, get lumbar support; I like “chairs” that can help build your core but you’ll want to stand, sit, work your core all in moderation
  4. organization – make sure you can find what you need – pencils, files, highlighters
  5. refreshments – if it’s coffee, invest in a solution that works for you.  I like the aero press and am okay making a cup every two or three hours
  6. snacks – I can get by with peanuts and gum so I have a good stock of each
  7. screens in your window – maybe the weather is absolutely perfect some days and you want to breathe fresh air.  Keep your cats in and the bugs out by making sure your screens are in good shape
  8. reliable computer – having to deal with equipment issues every week is a nightmare.  Failing systems will cause you to miss meetings or worse, cause your colleagues to spend their time repeatedly helping your figure out if your mic is working today
  9. external hard drive – computers fail, back up your data
  10. good keyboard, mouse, and external monitor – don’t punish yourself by forcing your fingers to use the keyboard on the new Macbook – what a nightmare!  Get a nice external keyboard and a second monitor…and all the dongles (thanks, @AppleCares!)

 

Kernel 4.19 and GCC 4.8.5-16

I ran into an issue trying to compile my own kernel, as you may remember.  The error suggested that I didn’t meet the minimum requirements to compile a kernel.  Well, as it turns out, the programs that I *do* have installed meet the minimum requirements.  The error message says my compiler is a non-retpoline compiler.  retpoline is:

The kernel can protect itself against consuming poisoned branch target buffer entries by using return trampolines (also known as “retpoline”) [3] [9] for all indirect branches. Return trampolines trap speculative execution paths to prevent jumping to gadget code during speculative execution. x86 CPUs with Enhanced Indirect Branch Restricted Speculation (Enhanced IBRS) available in hardware should use the feature to mitigate Spectre variant 2 instead of retpoline. Enhanced IBRS is more efficient than retpoline[1].

We’ll save the spectre discussion for another day (or, just google it, there are plenty of resources to explain what it is).  Back to the issue at hand, I beg to differ that my compiler is a non-retpoline compiler.  Well, at least according to the minimum specs, my version meets the requirements.

Or, does it?  According to Marek Polacek, I need gcc-4.8.5-28.el7 or newer.

I’m running 4.8.5-16.el7

[root@cleveland linux-4.19.87]# yum info gcc
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
Installed Packages
Name : gcc
Arch : x86_64
Version : 4.8.5
Release : 16.el7
Size : 37 M
Repo : installed
From repo : anaconda
Summary : Various compilers (C, C++, Objective-C, Java, ...)
URL : http://gcc.gnu.org
License : GPLv3+ and GPLv3+ with exceptions and GPLv2+ with exceptions and LGPLv2+ and BSD
Description : The gcc package contains the GNU Compiler Collection version 4.8.
: You'll need this package in order to compile C code.

Let’s download and install a newer version.  I found one here: http://mirror.centos.org/centos/7/os/x86_64/Packages/

List its dependencies:

# rpm -qpR gcc-4.8.5-39.el7.x86_64.rpm
warning: gcc-4.8.5-39.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
/bin/sh
/bin/sh
/bin/sh
/sbin/install-info
/sbin/install-info
binutils >= 2.20.51.0.2-12
cpp = 4.8.5-39.el7
glibc-devel >= 2.2.90-12
ld-linux-x86-64.so.2()(64bit)
ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)
libc.so.6()(64bit)
libc.so.6(GLIBC_2.11)(64bit)
libc.so.6(GLIBC_2.14)(64bit)
libc.so.6(GLIBC_2.2.5)(64bit)
libc.so.6(GLIBC_2.3)(64bit)
libc.so.6(GLIBC_2.3.2)(64bit)
libc.so.6(GLIBC_2.3.4)(64bit)
libc.so.6(GLIBC_2.4)(64bit)
libc.so.6(GLIBC_2.7)(64bit)
libdl.so.2()(64bit)
libdl.so.2(GLIBC_2.2.5)(64bit)
libgcc >= 4.8.5-39.el7
libgcc_s.so.1()(64bit)
libgmp.so.10()(64bit)
libgomp = 4.8.5-39.el7
libgomp.so.1()(64bit)
liblto_plugin.so.0()(64bit)
libm.so.6()(64bit)
libmpc.so.3()(64bit)
libmpfr.so.4()(64bit)
libz.so.1()(64bit)
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PartialHardlinkSets) <= 4.0.4-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rtld(GNU_HASH)
rpmlib(PayloadIsXz) <= 5.2-1

Or, get a more useful list by trying to install the rpm:

# rpm -Uvh gcc-4.8.5-39.el7.x86_64.rpm
warning: gcc-4.8.5-39.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
error: Failed dependencies:
cpp = 4.8.5-39.el7 is needed by gcc-4.8.5-39.el7.x86_64
libgcc >= 4.8.5-39.el7 is needed by gcc-4.8.5-39.el7.x86_64
libgomp = 4.8.5-39.el7 is needed by gcc-4.8.5-39.el7.x86_64

A quick note on our options for solving this riddle.

Option 1: yum localinstall is for one package.  Dependencies will be downloaded from the enabled repositories only.

yum install /home/drd/Downloads/libgomp-0.2.5-1.el7_x86_64.rpm

This still maintains the integrity of the rpm and yum databases.

Option 2: For local dependencies use rpm -Uvh *  (when the package + all dependencies are in the same directory)

Option 3: rpm -ivh is used when you want to have two versions of the same library installed (or, if you are absolutely sure that no package by the same name is installed)

Lastly, yum always uses rpm -Uvh for install.

So, keep working through the list of dependencies until all rpms are found in the local directory:

# rpm -Uvh *.rpm
warning: cpp-4.8.5-39.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
error: Failed dependencies:
libstdc++ = 4.8.5-39.el7 is needed by gcc-c++-4.8.5-39.el7.x86_64
libstdc++-devel = 4.8.5-39.el7 is needed by gcc-c++-4.8.5-39.el7.x86_64

And finally…

# rpm -Uvh *.rpm
warning: cpp-4.8.5-39.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
Preparing... ################################# [100%]
Updating / installing...
1:libquadmath-4.8.5-39.el7 ################################# [ 5%]
2:libgcc-4.8.5-39.el7 ################################# [ 9%]
3:libstdc++-4.8.5-39.el7 ################################# [ 14%]
4:libstdc++-devel-4.8.5-39.el7 ################################# [ 18%]
5:libgfortran-4.8.5-39.el7 ################################# [ 23%]
6:libgomp-4.8.5-39.el7 ################################# [ 27%]
7:cpp-4.8.5-39.el7 ################################# [ 32%]
8:gcc-4.8.5-39.el7 ################################# [ 36%]
9:libquadmath-devel-4.8.5-39.el7 ################################# [ 41%]
10:gcc-gfortran-4.8.5-39.el7 ################################# [ 45%]
11:gcc-c++-4.8.5-39.el7 ################################# [ 50%]
Cleaning up / removing...
12:gcc-gfortran-4.8.5-16.el7 ################################# [ 55%]
13:gcc-c++-4.8.5-16.el7 ################################# [ 59%]
14:libquadmath-devel-4.8.5-16.el7 ################################# [ 64%]
15:gcc-4.8.5-16.el7 ################################# [ 68%]
16:libgfortran-4.8.5-16.el7 ################################# [ 73%]
17:libstdc++-devel-4.8.5-16.el7 ################################# [ 77%]
18:libstdc++-4.8.5-16.el7 ################################# [ 82%]
19:libgcc-4.8.5-16.el7 ################################# [ 86%]
20:libquadmath-4.8.5-16.el7 ################################# [ 91%]
21:cpp-4.8.5-16.el7 ################################# [ 95%]
22:libgomp-4.8.5-16.el7 ################################# [100%]

And, we’re in business!  My make rpm-pkg works for compiling my kernel.

Bash Basics

basename & DIRNAME

basename "$FILE"
filename="$(basename $FILE)"
path="$(dirname $FILE)"

BOOLEAN

Bash doesn’t have built-in true and false boolean values that can be used to test for validity.  It’s often just as clear to use 0 or 1 for false and true, respectively.
“For interactive use, like one-liners, make sure to leave a space after !, or it will do history expansion. ((! foo)) works, so does ! ((foo)). ((foo || bar)) works as expected”[1].  Also, remember that bash considers an exit code of 0 success while an exit code of 1 is failure.  If you are working with true being equal to 1 and false being equal to 0 your may have to adjust some bash-y expectations.

#!/bin/bash

flag=0
if (( flag )); then
  echo "Condition is true";
fi

false=0
true=1

((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true

(Source: stackoverflow.com)


conditionals

if...then...elif...else...fi

if TEST-COMMAND1; then
  STATEMENTS1
elif TEST-COMMAND2; then
  STATEMENTS2
elif [ $test_var -ge 3 -a $test_var -lt 11 ]; then
  STATEMENTS3
else 
  STATEMENTS4
fi

FIND

Find files that have been changed in the last 24 hours.

find /var/lib/ -mtime -1 -ls

-1 means anything changed one day or less ago.  +1 means anything that has changed at least one day ago.  Having only 1 means exactly one day ago.

(Source: stackoverflow.com)


LOOPS

Loop through a list of files.

while read patch; do
  patch -p1 -f < ${patch} &>> $PATCH_LOG
done < <(ls -l ${PATCH_SUBDIR}/*.patch)

parameter expansions (substitutions)

For even more reference, see the holy grail of bash PEs.

Replace a character in a string with another character.

$ VER=4.19.87
$ NEWVER="${VER//./_}"
$ echo "$NEWVER"
4_19_87

PATHS

When you experience odd behavior with finding or not finding an installed or uninstalled program, you may need to refresh your remembered locations.

hash -r

See the following references: man hash | pip3 is looking for a wrong path | what does hash -r command do? | what is the purpose of the hash command


PRINTING

printf an exclamation mark.

Option 1: use single-quoted strings.

printf 'Unable to find config file.  Exiting!'

Option 2: add to the end of the string, outside of the quotes

printf "Unable to find config file.  Exiting" !

Option 3: print ASCII representation of ! character

printf "\041"

Option 4: Mix double quotes with single quotes (extension to Option 1)

printf "Unable to find config file.  Exiting"'!'" (that prints an exclamation mark just fine.\n"

Option 5: use a format string

printf "This (%s) is an exclamation mark%s\n" ! !

printf Two Variables
printf "%s %s" % "${MYSTRING1}" "${MYSTRING2}"

 


sequence

for i in $(seq 1 $END); do printf "$i\n"; done

 


set

The Set Built-in

Unless specifically required to not be, the second line of all Bash scripts ought to be: set -euf -o pipefail

Echo on for single command
set -x
ls $mydir
set +x

NOTE: In Jenkins, set -x is the default.  This echoes all commands.  Override by putting the following at the top of the “Execute Shell” build step:

#!/bin/bash +x

Strings

Change Case – Lower/upper

(I do not use “downcase” – it’s “lower case – that word needs to be exonerated from its duty of describing a letter case.)

Entire Word

I’m not even going to try to out-do this answer.

Though I will drop it here for quick reference:

$ myvar="True"
$ echo "$myvar" | tr '[:upper:]' '[:lower:]'
true

First Letter

foo="$(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})${foo:1}"
String Substitution

Again, great post on StackOverflow.  Dropping it here for quick reference.

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo "${firstString/Suzi/$secondString}" 
# prints 'I love Sara and Marry'

Style guide

Formatting

shfmt

$ shfmt -w -i 4 -sr <file(s) or path...>

How-to

man page
TLDP BASH Programming How-To
TLDP Advanced Bash-Scripting Guide
Google’s Shell Style Guide

Linter

shellcheck

Tests

-f Check if a file exists

FILE=/tmp/myfile.txt
if [[ -f "$FILE" ]]; then
  echo "$FILE exists"
fi

-z Check if a variable is empty

if [ -z "$var" ]; then
  printf '$var is empty\n';
else
  printf '$var is not empty\n';
fi

SUDO

One-liner including password

NOTE: Do not use this if you do not want others to see your script or bash history, which includes your user’s sudo password.

Use case: SSH to a Clonezilla Server that is already running and already has a specific user created on it.  This user can SSH but the files on the Clonezilla live system are read-only so you can not edit /etc/sudoers file to specify NOPASSWD for your user.

$ echo <password> | sudo -S <command>

Trickster: to avoid exposing the password on the command history, start the command with a SPACE character.

Check for sudo in your shell scripts
if [ $(id -u) -eq 0 ]; then
  printf "You are root\n"
else
  printf "You are not root\n"
fi

trap

For easier cleanup, use trap

 


Variables

Default Value
MyVar="${DEPLOY_ENV:-default_value}"

Git Stuff

Storing Screts

“Why should I worry? Our GitHub repository is private, and only our team can access the secrets!”

— Safelyl Storing Secrets in Git
EmbeddedArtistry.com

Even if you don’t want to concern yourself with it, consider it a challenge to keep your secrets secret.


Tips & Tricks

Tell git which key to use

Same way you tell any SSH session which key to use.

# vi ~/.ssh/config

Host github.com
    Hostname %h
    User git
    IdentityFile ~/.ssh/github_id_rsa

Workflow

Update Local with Remote

Use git pull --rebase

Rebase Local Master with Remote Master
git stash                               # optional
git checkout master
git fetch origin
git rebase origin/master