свіженьке з bash’тану

| комп'ютери, linux, bash, підказка

назбиралося трохи коротеньких рецептів для командного рядка linux (найчастіше специфічно для bash).

зміст

найжадібніші процеси

top показує купу цікавої та корисної інформації, але часом від нього треба лише невеличкий — три чи п’ять рядків — перелік процесів, які найбільше навантажують процесор. це просто:

lines=3; header=1; top -n 1 -o %CPU | tail -n+$((8-header)) | head -n$((lines+header))

lines вказує, скільки рядків вивести; header — чи показувати заголовок (0 або 1).

невелика модифікація (-o %MEM замість -o %CPU) виведе найжадібніші до пам’яті процеси.

таймер і нагадування

інколи потрібно поставити собі нагадування про щось. ось простий рецепт таймера з використанням sleep (можна додатифункцію собі до ~/.bashrc):

timer () {
    delay=$1
    shift
    sleep $delay ; notify-send "Timer" "$*\n($(date +'%Y-%m-%d, %H:%M'))" --expire-time 0 > /dev/null &
}

приклад використання:

timer 5m "Не забути вимкнути духовку!"

перший параметр — затримка, у форматі sleep — тобто секунди за замовчуванням, або хвилини (5m), години (2h) чи дні (1d), але комбінувати не можна (себто щось на кшталт sleep 5m1s не працюватиме).

інший рецепт дозволяє більш гнучно виставити нагадування на певний час, ба навіть дату, використовує команду at (потребує встановлення):

reminder () {
    runat=$1
    shift
    echo "notify-send 'Reminder' '$*\n($(date +'%Y-%m-%d, %H:%M')) --expire-time 0" | at $runat
}

приклад використання:

reminder "20:03 2019-09-10" "Не забути погодувати кота!"

перший параметр — час у форматі at, який дозволяє вказати час і дату.

поновлення оренди ip

рідко, але буває, що необхідно поновити оренду мережевої адреси ip (dhcp lease). для цього є команда dhclient, але спершу треба визначитися з мережевим адаптером:

ADAPTER=$(ip address | grep -E 'inet .*scope global' | grep -oE '[^ ]+$'); echo $ADAPTER

далі скасовуємо і поновлюємо оренду адреси ipv4:

sudo dhclient -v -r $ADAPTER
sudo dhclient -v -4 $ADAPTER

публічна адреса ip?

простий спосіб з dig (якщо утиліту встановлено):

dig +short myip.opendns.com @resolver1.opendns.com

нові дистрибутиви на базі arch не мають dig, але мають drill (який не підтримує опції +short):

drill myip.opendns.com @resolver1.opendns.com | grep "^myip.opendns.com" | cut -f5 -s

rsync — новий cp

cp надто мовчазний, — але замість нього можна використати rsync з ключиком --info=progress2 для простого копіювання з індикатором поступу:

rsync -ah --info=progress2 <source_dir> <target_file>

однак доведеться запам’ятати один нюанс: rsync не потребує ключика -r, зате вимагає явно вказати, якщо джерело є текою:

rsync -ah --info=progress2 <source_dir>/* <target_dir>

шаблон для скриптів bash

лінуюсь щоразу пригадувати, як воно правильно пишеться, тож:

#!/usr/bin/env bash
# tivasyk <tivasyk@gmail.com>
#
# a simple template for starting bash scripts

# bash options
set -o errexit
set -o nounset
set -o pipefail
[[ "${TRACE:-0}" == "1" ]] && set -o xtrace

# variables
local LIBRARY=("library.sh")

# /i\ set your global variables here
# ...

# functions
show_help() {
    local NAME=$(basename $0)
    cat <<EOF
$NAME is a template file for writing (somewhat) more complex bash scripts.

USAGE:
  $NAME [--trace] <new_script>

OPTIONS:
  -h, --help    Show a short usage hint (this text)
  --trace       Trace the script (sets the xtrace option for Bash)
                (you probably want this to be the first argument);
                alternatively set TRACE variable to 1 before running
                the script.
EOF
}

start_dir() {
  # detect the script's starting directory (the clever way, works with links)
  local SOURCE="${BASH_SOURCE[0]}"
  while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
    local DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
    SOURCE="$(readlink "$SOURCE")"
    # if a relative symlink, resolve it relative to the path where the symlink file was located
    [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"    
  done
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
  echo "${DIR}"
}

parse_arguments() {
  # process command-line arguments
  while [[ "$*" != "" ]]; do
    case "$1" in
      "-h"|"--help" )
        show_help
        exit 0
        ;;
      "--trace" )
        set -o xtrace
        shift
        ;;
      # /i\ your options arguments go here
      # ...
      * )
        # /i\ your other arguments parsing code goes here
        shift
        ;;
    esac
  done
}

  # /i\ your functions go here
  # ...

main() {
  parse_arguments "$@"
  for LIB in "${LIBRARY[@]}"; do
    if [[ -f "$(start_dir)/${LIB}" ]]; then
      source "$(start_dir)/${LIB}" 
    fi
  done
  # /i\ your main code goes here
  # ...
}

main "$@"

далі буде…