Script to notify myself

Sharing a simple script I recently used. Because recent development, I need to run a build command which needs about 2-3 minutes. I run it in the background so that I can continue my other work.

But switching the window in order to check whether the build is completed, it is frustrating. To make it easy, I wrote the following script,

#!/bin/bash

notify-send -t 1500 -i applications-utilities "$@"
aplay /usr/share/orage/sounds/Knock.wav &> /dev/null

notify-send comes from the libnotify package. By running this command, you will see a popup notification on your desktop.

/usr/share/orage/sounds/Knock.wav comes from orage package. I just simply searched for a wave file that sounds good to me.

So, once I run the command such as,

build_my_project && /path/to/script/notify_me.sh

Once the project is built, my desktop will notify me with popup notification and play a sound.

Rename files according to date

I recently wrote a Perl script, that renames the files in a directory according to the date, in the format “YYYYMMDD ##” where “##” is the running number.

Rationale

Because I used to download the photos using the mobile apps like Weibo or Twitter, however the file names are almost random. This made me hard to organize these photos on my computer.

The artists (or celebrities) usually share a set of their photos, so when I download these photos, the files should have mtime (modified time) in the correct order.

Yet, I don’t need to rename the file to the time precision like “HH:MM:SS”. I just need the date and followed by the running number, because it looks shorter.

Though we can just use the file browsers to sort the files according to the time, it is still inconvenient to browse the images by changing the sorting condition. Furthermore, mtime can be changed, and this will void the purpose of the sorting.

Lastly, the randomized filename is just meaningless to me. Rename them according to the date is much more useful, in my opinion.

 

Script


#!/usr/bin/perl -w
use strict;
use warnings;
use POSIX 'strftime';
use File::Basename;
sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
sub get_files_from_directory {
my $path = $_[0];
opendir my $dir, $path or die "Cannot open directory: $!";
my @files = readdir($dir);
@files = grep(!/^\.$|^\.\.$/, @files);
closedir $dir;
return @files;
}
sub group_files_by_date {
my ($dir, @files) = @_;
my %groups = ();
foreach my $file (@files) {
my $timestamp = (stat "$dir/$file")[9];
my $date = strftime("%Y%m%d", localtime($timestamp));
if (not exists $groups{$date}) {
@{$groups{$date}} = ();
}
push @{$groups{$date}}, "$dir/$file";
}
return %groups;
}
sub sort_files_in_groups {
my (%groups) = @_;
foreach my $key (sort keys %groups) {
my $files = $groups{$key};
@{$groups{$key}} = sort { (stat $a)[9] <=> (stat $b)[9] } @{$files};
}
return %groups;
}
sub build_new_name {
my ($date, $num, $dir, $ext) = @_;
my $zero_num = sprintf("%02d", $num);
return "$dir${date} ${zero_num}${ext}";
}
sub name_pairs {
my (%groups) = @_;
my %pairs;
foreach my $key (keys %groups) {
my $files = $groups{$key};
for (my $i = 0; $i < scalar @{$files}; $i++) {
my $file = @{$files}[$i];
my ($basename, $dir, $ext) = fileparse($file, qr/\.[^.]*/);
my $new_name = &build_new_name($key, $i + 1, $dir, $ext);
$pairs{$file} = $new_name;
}
}
return %pairs;
}
sub print_pairs {
my (%pairs) = @_;
foreach my $key (keys %pairs) {
print "$key\t->\t$pairs{$key}\n";
}
}
sub rename_files {
my (%pairs) = @_;
foreach my $key (keys %pairs) {
rename $key, $pairs{$key};
}
}
sub save_log {
my (%pairs) = @_;
my $file = 'rename_files_to_date.log';
open(my $fh, '>', $file);
foreach my $key (keys %pairs) {
print $fh "$key\t->\t$pairs{$key}\n";
}
close $fh;
}
sub main {
my @argv = @_;
my $dir = $argv[0];
my @files = &get_files_from_directory($dir);
my %groups = &group_files_by_date($dir, @files);
%groups = &sort_files_in_groups(%groups);
my %pairs = &name_pairs(%groups);
&print_pairs(%pairs);
print "\nWARNING: Rename is irreversible. Recommend to make a backup.\n",
"Confirm rename files? (y/N) ";
my $choice = <STDIN>;
print $choice;
exit 0 unless trim($choice) eq 'y';
&rename_files(%pairs);
&save_log(%pairs);
print "Done. Log file also saved.\n";
}
&main(@ARGV);

 

Why Perl?

In my opinion, Perl is less famous like Python in the present day. But I prefer to use Perl, due to the popularity in most Linux distribution. For example Perl is the base package of Arch Linux. Once I installed Arch Linux, I can run Perl script immediately.

Though Python is great, backward incompatibility sometimes causes issue, which I may need to maintain the script. If I write with Perl, I can pay less effort to maintain the script.

 

WARNING! And usage

As the script mentioned,

Rename is irreversible. Recommend to make a backup.

The usage is,

./rename_files_to_date.pl ./target_dir

Where target_dir is the directory that contains the files you want to rename. It will not rename the files recursively.

PLEASE USE THE SCRIPT AT YOUR OWN RISK!!!

After renaming the files, a log file will be created. It is used just in case you want to revert the file name. (But you have to do this manually.)

Monty Hall problem and frog riddle

Monty Hall paradox

Probability topic is the fundamental concept of the statistics. And machine learning is closely related to statistics. That is why, understand the probability very important if you are doing research, statistics, and machine learning.

Monty Hall is a very interesting problem. It says, if you are given 3 doors to choose. One of them contains a car (which you want), the other two are goats (which you don’t want). After you made your choice, before opening the door, the host will open the door that you didn’t choose yet contains the goat (he knows which door has the goat). Now, if you are given an opportunity to change your choice to another door (which you didn’t choose earlier), are you going to change?

In the first glance, you will feel that whatever you choose, the probability is always 1/3. However, the conditional probability tells you that, if you always make the switch after the host opened the door that has a goat, your probability to win the car will increase to 2/3. What??

In order to prove this, I wrote a Python script.

#!/usr/bin/env python
# This is simulating Monty Hall Paradox

import random


def monty(switch):
    # random shuffle
    doors = [0, 0, 1]  # one of the door contains the car
    random.shuffle(doors)

    openDoor = None

    # choose the first door (not open)
    # if the first door is 1, randomly open the other
    if doors[0] == 1:
        # open the door
        openDoor = random.randint(1, 2)
    else:  # open the door that contains goat
        if doors[1] == 1:
            openDoor = 2
        else:
            openDoor = 1

    # now open the last door
    if not switch:
        return doors[0]
    else:
        if openDoor == 2:
            return doors[1]
        else:
            return doors[2]


def main():
    total = 10000
    car = 0
    for i in range(total):
        car += monty(True)

    print("Always switch the door. Total: {}, car: {}. P = {}".format(total, car, car / total))

    car = 0
    for i in range(total):
        car += monty(False)

    print("No switch the door. Total: {}, car: {}. P = {}".format(total, car, car / total))


main()

Run the code, you will always get the probabilty close to 0.6667 if you always switch the door.

Always switch the door. Total: 10000, car: 6625. P = 0.6625
No switch the door. Total: 10000, car: 3309. P = 0.3309

Frog riddle

Recently I just watched a Youtube about frog riddle.

It also mentions about the conditional probability. Interestingly, quite a lot of comments mentioned that the author is wrong.

In order to prove that the author is correct, I wrote another Python script.

#!/usr/bin/env python

import random

# Frog 0 for female, 1 for male


def create_frog():
    return random.randint(0, 1)


def has_croak(pairs):  # also male
    return 1 in pairs


def has_female(frogs):
    return 0 in frogs


def choose_without_croak(choose_two):
    frogs = [create_frog() for i in range(3)]
    # first frog at the right side
    # second and third at the left side

    if choose_two:
        return has_female(frogs[1:])  # choose two frogs

    return has_female(frogs[0:1])


def main():
    total = 10000
    correct = 0
    for i in range(total):
        correct += choose_without_croak(True)
    print('Just choose two frogs. Total: {}, correct: {}. P = {}'.format(total, correct, correct / total))

    correct = 0
    for i in range(total):
        correct += choose_without_croak(False)
    print('Just choose one frog. Total: {}, correct: {}. P = {}'.format(total, correct, correct / total))


# The exact question is,
# "What is the probability of the frogs in the pair has female,
# given that one of them is male?"
def exact_calculation():
    total = 10000
    croak = 0
    correct = 0
    for i in range(total):
        frogs = [create_frog() for i in range(3)]
        if has_croak(frogs[1:]):
            croak += 1
            if has_female(frogs[1:]):
                correct += 1
    print('Total croak: {}, correct: {}. P = {}'.format(croak, correct, correct / croak))


main()
exact_calculation()

Running the script, you will get

Just choose two frogs. Total: 10000, correct: 7498. P = 0.7498
Just choose one frog. Total: 10000, correct: 4974. P = 0.4974
Total croak: 7474, correct: 4998. P = 0.6687182231736687

Based on the result, if you choose two frogs, the probability of survive is close to 0.75. If you choose one frog, the probability is 0.5.

Now, the tricky part is the probability 0.67 mentioned in the video. The question should be “What is the probability of the frogs in the pair has female, given that one of them is male?”

So, based on the question, my similuation needs to get the total count of the male (that has croak), and within these pairs, count the female frogs.

To convert this into mathematical expression,

P(\text{female frog}) = 0.5

P(\text{at least one male frog}) = 0.75

P(\text{female frog} | \text{at least one male frog}) = \frac{0.5}{0.75} = 0.6667

Then, based on the simulation and calculation, you will get the 0.6667.

Switching display/monitor/screen in Linux

Because I am using the Openbox (window manager), and I believe that the laptop Fn+F8 (or whatever combination with Fn) doesn’t work properly on Linux. Because the combination is detected as Super+p (aka Win+p). As a result, I wrote a Perl script to solve the switching display/monitor/screen issue on my laptop.

#!/usr/bin/perl

# This script requires xrandr, and several bash script created by arandr

use strict;
use warnings;

# Edit these global variables based on your setting
my $primary = 'eDP1';
my $secondary = 'HDMI1';

my %scripts = (default => 'default.sh',
               external_only => 'large_only.sh',
               clone => 'clone.sh',
               dual => 'dual.sh');
my $script_path = '~/.screenlayout';
# End edit

sub get_xrandr {
    return `xrandr`;
}

sub is_active {
    my ($monitor) = @_;
    for my $i (0 .. (scalar @$monitor - 1)) {
        my $line = $monitor->[$i];
        if ($line =~ /\*/) {
            return 1;
        }
    }
    return 0;
}

sub is_left {
    my ($monitor) = @_;
    my $line = $monitor->[0];
    if ($line =~ /\d+x\d+\+(\d+)\+\d+/) {
        if ($1 > 0) {
            return 0;
        }
    }
    return 1;
}

sub is_default {
    my ($primary, $secondary) = @_;
    return &is_active($primary) && !&is_active($secondary);
}

sub is_external_only {
    my ($primary, $secondary) = @_;
    return !&is_active($primary) && &is_active($secondary);
}

sub is_clone {
    my ($primary, $secondary) = @_;
    return &is_active($primary) && &is_active($secondary) &&
        &is_left($primary) && &is_left($secondary);;
}

sub is_dual {
    my ($primary, $secondary) = @_;
    return &is_active($primary) && &is_active($secondary) &&
        &is_left($primary) && !&is_left($secondary);;
}

sub get_monitor_style {
    my ($primary, $secondary) = &get_monitor_details;
    if (&is_default($primary, $secondary)) {
        return 'default';
    }
    elsif (&is_clone($primary, $secondary)) {
        return 'clone';
    }
    elsif (&is_dual($primary, $secondary)) {
        return 'dual';
    }
    elsif (&is_external_only($primary, $secondary)) {
        return 'external_only';
    }
    return 'unknown';
}

sub set_monitor_style {
    my ($style) = @_;
    my $script =  join('/', $script_path, $scripts{$style});
    my $cmd = "sh $script";
    `$cmd`;
}

sub switch_next_monitor_style {
    my $current_style = &get_monitor_style;
    if ($current_style eq 'default') {
        &set_monitor_style('external_only');
    }
    elsif ($current_style eq 'external_only') {
        &set_monitor_style('dual');
    }
    elsif ($current_style eq 'dual') {
        &set_monitor_style('clone');
    }
    elsif ($current_style eq 'clone') {
        &set_monitor_style('default');
    }
    else {
        print STDERR "Unknown monitor style";
    }
}

sub switch_prev_monitor_style {
    my $current_style = &get_monitor_style;
    if ($current_style eq 'default') {
        &set_monitor_style('clone');
    }
    elsif ($current_style eq 'external_only') {
        &set_monitor_style('default');
    }
    elsif ($current_style eq 'dual') {
        &set_monitor_style('external_only');
    }
    elsif ($current_style eq 'clone') {
        &set_monitor_style('dual');
    }
    else {
        print STDERR "Unknown monitor style";
    }
}

sub switch_monitor_style {
    my ($prev) = @_;
    if ($prev) {
        &switch_prev_monitor_style;
    }
    else {
        &switch_next_monitor_style;
    }
}

sub get_monitor_details {
    my $xrandr = &get_xrandr;
    my @lines = split(/\n/, $xrandr);

    my @primary_lines;
    my @secondary_lines;
    my $current_block;
    for my $i (0 .. $#lines) {
        my $line = $lines[$i];
        if ($i == 0) {
            next;  # not "continue"
        }
        if ($line =~ /^${primary}/) {
            $current_block = 'primary';
        }
        elsif ($line =~ /^${secondary}/) {
            $current_block = 'secondary';
        }
        if ($current_block eq 'primary') {
            push @primary_lines, $line;
        }
        elsif ($current_block eq 'secondary') {
            push @secondary_lines, $line;
        }
    }
    return (\@primary_lines, \@secondary_lines);
}

sub main {
    my ($prev) = @_;
    &switch_monitor_style($prev);
}

&main(@ARGV);

The script requires “xrandr” command. Furthermore, you need to have some actual switching monitor bash script, which can be created by using ARandR. Example of the script

#!/bin/sh
xrandr --output HDMI1 --primary --mode 1920x1080 --pos 0x0 --rotate normal --output VIRTUAL1 --off --output eDP1 --off

So, my Perl script will detect existing screen setup, whether it is laptop only (“default”), external only (“external_only”), laptop with external monitor at the right side (“dual”), or clone (“clone”) for both monitor sharing same screen. Therefore, we need to create four bash scripts using ARandR for these settings.

To invoke the script,

perl /path/to/monitor_switch.pl

This will switch to the screen to the “next” setting, in this order: default -> external_only -> dual -> clone -> default.

In order to switch between default and external_only, I extended the script with an argument.

perl /path/to/monitor_switch.pl prev

When passing with an argument (any argument), the monitor setup will switch in the reverse order: default -> clone -> dual -> external_only -> default. By this, we can switch between default and external_only easily.

Next, just apply the keybinding (aka hotkey or shortcut) to your preferred combination, then you can switch the screen with your favourite key combination.

Yeay!

P/S: The reason I wrote this script is, when I show my screen on external only, and the power is cut, the screen doesn’t switch to laptop automatically. That means, I cannot see anything to change my screen display. Before the script is written, I blindly use the Terminal, Ctrl+R, and type the keyword and press Enter to switch back. But this is extreemly impractical.

Joining video parts together

Have you downloaded the videos online, such as Youku, Tudou, or even YouTube? Have you downloaded the videos which the uploaders split the them into several parts?

Whatever your answer is, you may face the same problem as me.

I downloaded the videos to watch later. But the videos are split into several parts. I wish to watch it as a whole (because it should be one big file). So, I created this script to solve the problem. This script requires MP4Box (in the gpac package) and FFmpeg.

To use the script,

./video_join.sh 'video_part*.mp4' "video.mp4"

where the first argument is same as the “find . -iname ‘video_part*.mp4′”, so that if the files are video_part1.mp4, video_part2.mp4, video_part3.mp4, …, they will be joined together; 2nd argument is the output file. It will use the MP4Box to join the file, which is fast.

However, sometimes the videos we downloaded are FLV format. This is solved by FFmpeg, but it will convert to MP4 as well, and the conversion is slow.

./video_join.sh -flv 'video_part*.flv' "video.mp4"

Just add “-flv”, it will use FFmpeg to convert and join the FLV videos into MP4. Actually, it not only converts from FLV, but any format supported by FFmpeg.

Therefore, if you downloaded a series of videos, and each series are split, then you may

for i in {01..20} ; do cmd=`echo "./video_join.sh 'video${i}_part*.mp4' \"video${i}.mp4\""` ; sh -c "$cmd" ; done

The command above will convert and join the video01_part*.mp4 into video01.mp4, video02_part*.mp4 into video02.mp4, …, and so on until video20.mp4.