Scripting basics

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the bash category.

Last Updated: 2024-05-07

How to write a function

setup_database() {
  createdb ...
}

# call it... note that is uses no parens
setup_database

How to do an if statement based whether a command exists

if ! command -v xcode-select &> /dev/null; then
  xcode-select --install
  else
  echo "-> Skipping xcode-select because it was already available."
fi

How to do an if statement based on file presence

if ! test -f .env; then
  echo "No .env file found in project root."
else
  echo "ask colleage for env file"
fi

How to do an if statement based on numerical value

# not equal
if [[ $var -ne 0 ]]
  echo "not equal 0"
else
  echo "equal 0"
fi

reverse

# equal
if [[ $var -eq 0 ]]
  echo "equal 0"
else
  echo "not equal 0"
fi

How to do an if statement based on string inclusion.

string="abcd"
if [[ $string == *"cd"* ]]; then
  echo "It's there!"
fi

Use != for the opposite.

How to pass on all arguments from script

# inside a script to wrap manage.py
python mysite/manage.py "$@"

If you source a file containg exports, are they available in rest of the script?

Yep, for sure

# .env contain FOO=1
source .env
# $FOO is now available

How to break out into multiple files

Use slashes

gh pr list | \
  cut -f 3 | \
  xargs -I {} sh -c \
  'echo Listing potential conflicts for branch "$1": && git diff HEAD...origin/$1 --name-only -- mobile-app && echo ' \
  sh {}

How to convert many lines of output to a single line

Say you have this:

$ git diff --name-only HEAD...optimize-header-space-follow-up
mysite/static/react-app/src/api/savedSearches.ts
mysite/static/react-app/src/api/stores/filters.js
mysite/static/react-app/src/components/Common/Filters/CategoryFilter/definitions.ts
mysite/static/react-app/src/components/ContextTabs/ContextTabs.tsx
mysite/static/react-app/src/components/experimental/Breakpoint/Breakpoint.tsx
mysite/static/react-app/src/components/experimental/Chip/Chip.module.scss
mysite/static/react-app/src/components/experimental/Layouts/Grid.module.scss
mysite/static/react-app/src/constants/user.ts
mysite/static/react-app/src/pages/Project/SavedSearches/components/Header.tsx
mysite/static/react-app/src/pages/Project/SavedSearches/index.tsx
mysite/static/react-app/src/pages/Project/Search/helpers.ts
mysite/static/react-app/src/pages/SavedSearches/SavedSearches.tsx
mysite/static/react-app/src/pages/SavedSearches/components/Header.tsx

You can convert this to a single line with echo (handles new lines, tabs, multiple spaces)

$ echo $(git diff --name-only HEAD...optimize-header-space-follow-up)
mysite/static/react-app/src/api/savedSearches.ts mysite/static/react-app/src/api/stores/filters.js mysite/static/react-app/src/components/Common/Filters/CategoryFilter/definitions.ts mysite/static/react-app/src/components/ContextTabs/ContextTabs.tsx mysite/static/react-app/src/components/experimental/Breakpoint/Breakpoint.tsx mysite/static/react-app/src/components/experimental/Chip/Chip.module.scss mysite/static/react-app/src/components/experimental/Layouts/Grid.module.scss mysite/static/react-app/src/constants/user.ts mysite/static/react-app/src/pages/Project/SavedSearches/components/Header.tsx mysite/static/react-app/src/pages/Project/SavedSearches/index.tsx mysite/static/react-app/src/pages/Project/Search/helpers.ts mysite/static/react-app/src/pages/SavedSearches/SavedSearches.tsx mysite/static/react-app/src/pages/SavedSearches/components/Header.tsx mysite/static/react-app/src/services/constants/misc.ts mysite/static/react-app/src/types/search.ts

Test numeric output of another command with inequality

Sub [[ and a subshell.

if [[ $(du -k mysite/static/react-app/build/bundle.js) -gt 3000 ]]; then
  echo "File is too big"
  echo
  exit 2
else
  echo "File is small enough"
  exit 0
fi

Note that passing the internal bit to /dev/null will cause not output to top-shell, breaking things. if [[ $(du -k mysite/static/react-app/build/bundle.js 1>/dev/null) -gt 3000 ]]; then

How to do something on every item in an array

# Declare an array. Parentheses. No commas
# Note that there must be no space before or after the equal sign
branches=(
  change-us-region-to-nam-2453
  dashbaord-date-range-filters-3170
  persist-store-analysis-2413
  validate-traits-2907
  import-2-polish-2617
  import-dam-sire-2901
)

# Use a for loop with syntax - `${array_name[@]}`
for branch in "${branches[@]}"
do
   echo "Working on $branch"
done

How to make bash loop skip to next item if there is a failure

for branch in "${branches[@]}"
do
   # Use || continue. Otherwise, by default, it will try next command in the loop
   command_that_might_fail || continue
   echo "Working on $branch"
done

How to add a heredoc (without expansion) to a file

cat <<- "DELIMITER" > pyproject.toml
[tool.isort]
profile = "black"
skip_gitignore = true
line_length = 120
skip = ["site-packages"]
skip_glob = ["*/migrations/*"]

[tool.black]
line-length = 120
target-version = ['py311']
include = '\.pyi?$'
skip-string-normalization = true
exclude = '''
/(
  | migrations
  | site-packages
)/

'''
DELIMITER

How to replace commands with newlines

pbpaste | tr "," "\n"

How to get current filename

current_filename=$(basename "$0")