Bash


NetCat (nc) as a webserver

Recently, we needed to perform some tests in a network. Specifically, we wanted to check the configuration of a firewall and see what IP are blocked and/or which ports are allowed to go through. To do so, we used NetCat to setup a small web-server to perform our tests.

Netcat (often abbreviated to nc) is a computer networking utility for reading from and writing to network connections using TCP or UDP. Netcat is designed to be a dependable back-end that can be used directly or easily driven by other programs and scripts. At the same time, it is a feature-rich network debugging and investigation tool, since it can produce almost any kind of connection its user could need and has a number of built-in capabilities.

From: https://en.wikipedia.org/wiki/Netcat

while true;
do
  echo -e "HTTP/1.1 200 OK\r\n\r\n<h1>$(hostname) is live</h1>$(date)" | nc -vl -p 5555;
done

or in one line

while true; do echo -e "HTTP/1.1 200 OK\r\n\r\n<h1>$(hostname) is live</h1>$(date)" | nc -vl -p 5555; done

Explanation of code:

  • The above code creates an infinite loop that calls nc in listening mode, we had to do this as nc will terminate as soon as it serves one client.
  • Using echo we create an html 200 response along with a small “webpage”.
  • While generating the webpage, echo -e will execute the commands hostname and date to get the current system values adding them to the resulted text.
  • The resulted text is then piped to nc to be served as a response to any incoming clients.
  • The date and time that nc will show to the client is not the current date and time when visiting the webpage but the one that was when echo was executed.

nc parameters:

  • -v, --verbose Sets the verbosity level and it can be used several times to increase it even further
  • -l, --listen Instructs nc to bind and listen for incoming connections (just like a web-server)
  • -p, --source-portwith port parameter specifies the source port to be used by nc

Custom terminator layout with multiple tabs and terminals

The following terminator layout ( Terminator Layout (56 downloads) ) opens 3 different tabs, the first two tabs contain only one terminal each and the third one has 4 terminals in a 2×2 matrix.
Each of these tabs have their own custom name set and following each terminal has its name set to make it easier for the user to recognize the purpose of each one.

Terminator Layout (56 downloads)

After opening these terminals, the configuration file, contains specific commands to be executed by each terminal, allowing you to automate a some trivial part of your day to day operations.
In this example, each terminal will navigate to a specific project or connect via ssh to some server, then it will perform some operation like performing a git pull and finally it will preserve the connection for you by starting a new bash instance to continue using that terminal.

Feel free to edit the layout and create a custom configuration for your tabs / the terminals and the commands.

Installation / Usage

  1. Replace the config ( Terminator Layout (56 downloads) ) file in your home user folder ~/.config/terminator/  with the one we provide
    (In nautilus press Ctrl+H to view hidden files and folders if you cannot find the .config folder)
  2. Open terminator and execute the following:
    terminator -l init;

If you want to create an alias for this command:
Open .bashrc file at your home user folder and add the following

alias my-init="terminator -l init"

For any new terminal in terminator, executing my-init will spawn a new window of terminator that has all the configuration from the file loaded into it.

Contents of Terminator Layout (56 downloads)

[global_config]
[keybindings]
[layouts]
  [[default]]
    [[[child1]]]
      parent = window0
      type = Terminal
    [[[window0]]]
      parent = ""
      type = Window
  [[init]]
    [[[child0]]]
      fullscreen = False
      last_active_window = True
      maximised = True
      order = 0
      parent = ""
      position = 0:26
      size = 1918, 1002
      title = /bin/bash
      type = Window
    [[[child1]]]
      active_page = 0
      labels = www, MA, all other, dev logs, staging logs, live logs
      last_active_term = d3c317d7-964a-4625-96d0-39deb5166072, 93ce7874-059e-4794-b337-7b640654a3d6, db090e6f-07e4-431e-ad86-a8b6cb965b5e, 906a5f4d-a3af-4da8-8385-673b132e7edd, 1b48b3b9-216c-470b-be53-ec1e8c6fdc0b, cb7d737c-a064-4e0e-ad5e-59c47d7bdd3b
      order = 0
      parent = child0
      type = Notebook
    [[[child11]]]
      order = 3
      parent = child1
      position = 956
      ratio = 0.500261643119
      type = HPaned
    [[[child14]]]
      order = 4
      parent = child1
      position = 956
      ratio = 0.500261643119
      type = HPaned
    [[[child17]]]
      order = 5
      parent = child1
      position = 956
      ratio = 0.500261643119
      type = HPaned
    [[[child4]]]
      order = 2
      parent = child1
      position = 956
      ratio = 0.500261643119
      type = HPaned
    [[[child5]]]
      order = 0
      parent = child4
      position = 481
      ratio = 0.500520291363
      type = VPaned
    [[[child8]]]
      order = 1
      parent = child4
      position = 481
      ratio = 0.500520291363
      type = VPaned
    [[[terminal10]]]
      command = cd /vhosts/www.example.com/; git pull; bash
      order = 1
      parent = child8
      profile = default
      title = www.example.com
      type = Terminal
      uuid = db090e6f-07e4-431e-ad86-a8b6cb965b5e
    [[[terminal12]]]
      command = "ssh -t git 'cd vhosts/www.bytefreaks.net/ci_applications/registration_forms/logs/; ll; bash'"
      directory = ""
      order = 0
      parent = child11
      profile = default
      title = WWW dev logs
      type = Terminal
      uuid = 4c08356b-b516-4286-8b6d-ba071f1394f3
    [[[terminal13]]]
      command = "ssh -t git 'cd vhosts/my.bytefreaks.net/symfony/var/logs/; ll; bash'"
      directory = ""
      order = 1
      parent = child11
      profile = default
      title = MA dev logs
      type = Terminal
      uuid = 906a5f4d-a3af-4da8-8385-673b132e7edd
    [[[terminal15]]]
      command = "ssh -t ptl-web3 'cd /data/var/www/vhosts/staging-www.bytefreaks.net/htdocs/ci_applications/registration_forms/logs/; ls -la; bash'"
      order = 0
      parent = child14
      profile = default
      title = WWW staging logs
      type = Terminal
      uuid = 1b48b3b9-216c-470b-be53-ec1e8c6fdc0b
    [[[terminal16]]]
      command = "ssh -t ptl-web3 'cd /data/var/www/vhosts/staging-my.bytefreaks.net/htdocs/symfony/var/logs/; ls -la; bash'"
      order = 1
      parent = child14
      profile = default
      title = MA staging logs
      type = Terminal
      uuid = e26e94cd-855a-44ff-9c67-66b1c03bac56
    [[[terminal18]]]
      command = "ssh -t ptl-web3 'cd /data/var/www/vhosts/www.bytefreaks.net/htdocs/ci_applications/registration_forms/logs/; ls -la; bash'"
      directory = ""
      order = 0
      parent = child17
      profile = default
      title = WWW Live logs
      type = Terminal
      uuid = 70466609-2d01-45d2-84b4-e377b111e540
    [[[terminal19]]]
      command = "ssh -t ptl-web3 'cd /data/var/www/vhosts/my.bytefreaks.net/htdocs/symfony/var/logs/; ls -la; bash'"
      directory = ""
      order = 1
      parent = child17
      profile = default
      title = MA Live logs
      type = Terminal
      uuid = cb7d737c-a064-4e0e-ad5e-59c47d7bdd3b
    [[[terminal2]]]
      command = cd /vhosts/www.bytefreaks.net/; git pull; bash
      order = 0
      parent = child1
      profile = default
      title = www.bytefreaks.net
      type = Terminal
      uuid = d3c317d7-964a-4625-96d0-39deb5166072
    [[[terminal3]]]
      command = cd /vhosts/my.bytefreaks.net/; git pull; bash
      order = 1
      parent = child1
      profile = default
      title = my.bytefreaks.net
      type = Terminal
      uuid = 93ce7874-059e-4794-b337-7b640654a3d6
    [[[terminal6]]]
      command = cd /vhosts/www.michanicos.com/; git pull; bash
      order = 0
      parent = child5
      profile = default
      title = www.michanicos.com
      type = Terminal
      uuid = 2f204209-0c0b-4fab-b883-95f95f5d38e9
    [[[terminal7]]]
      command = cd /vhosts/www.etea.com.cy/; git pull; bash
      order = 1
      parent = child5
      profile = default
      title = www.etea.com.cy
      type = Terminal
      uuid = 6f801914-5225-4e1f-b54c-f48540274614
    [[[terminal9]]]
      command = cd /vhosts/www.ieee.org/; git pull; bash
      order = 0
      parent = child8
      profile = default
      title = www.ieee.org
      type = Terminal
      uuid = 3dbfe3a7-2e25-4e7d-bb02-dc4aeeeda47f
[plugins]
[profiles]
  [[default]]
    background_darkness = 0.8
    cursor_color = "#ffffff"
    foreground_color = "#ffffff"

How we create bootable GNU/Linux USB flash drives from terminal

A very important tool in our everyday life are the LiveUSB GNU/Linux flash drives.
We keep an updated collection of several GNU/Linux flavors/distributions (Fedora, CentOS, (L/X)Ubuntu, Kali etc.) that are used depending on the scenario.

The command we use is the following:

sudo dd bs=4M if=path/to/OS.iso of=/dev/sdX conv=fdatasync;

dd allows you to convert and copy a file and we use it to copy the ISO file of the operating system onto the USB flash drive.

Notes:

  1. You need to unmount the USB flash drive before formatting it, e.g.:
    sudo umount /dev/sdXY;
  2. You need to use the device filename and not a partition filename:
    e.g. You need to use /dev/sdX and NOT /dev/sdX1
  3. You need to use either the root account or execute the command with sudo
  4. If you do not know the filename associated with your flash drive, use an application like the following ones to determine which /dev file is mapped to the USB flash drive:
    gnome-disks; or
    lsblk; or
    sudo fdisk -l;

The parameters we use are the following:

  • bs=SIZE_IN_BYTES defines up to how many bytes should be read and written at a time.
    In our case we used 4 Megabytes (4M).
  • if=INPUT_FILE defines the file to be read, we use this parameter to point to the OS ISO file that we want to write on the USB drive.
  • of=OUTPUT_FILE defines the filename where the data is to be written in.
    In GNU/Linux, devices are accessible like files as well so we used /dev/sdX here that happened to be the device file assigned to our USB device.
  • conv=CONVS converts the file as per the comma separated symbol list
    fdatasync physically writes output file data before finishing, we use this parameter to be sure that all I/O operations are done well before dd terminates, this way we are certain that our USB device will be ready to use as soon as the application is done.

ffmpeg: Create a video countdown

The code below was used to generate the video countdown timers that are available in the following playlist using ffmpeg:

#This example will create a 3 second video, with 100 frames per second and it will print the elapsed and remaining times using a two second accuracy.
fps=100;
seconds=3;
mantissaDigits=2;
upperFont=600;
lowerFont=100;
ffmpeg -loop 1 -i ~/Pictures/Black-Background.png -c:v libx264 -r $fps -t $seconds -pix_fmt yuv420p -vf "fps=$fps,drawtext=fontfile='/usr/share/fonts/urw-base35/C059-Bold.otf':fontcolor=yellow:fontsize=$upperFont:x=(w-text_w)/2:y=(h-text_h)/2:text='%{eif\:($seconds-t)\:d}.%{eif\:(mod($seconds-t, 1)*pow(10,$mantissaDigits))\:d\:$mantissaDigits}',drawtext=fontfile='/usr/share/fonts/urw-base35/C059-Bold.otf':fontcolor=yellow:fontsize=$lowerFont:x=(w-text_w)/2:y=((h-text_h)/2)+$upperFont:text='Elapsed\: %{eif\:(t)\:d}.%{eif\:(mod(t, 1)*pow(10,$mantissaDigits))\:d\:$mantissaDigits}'" "$seconds seconds countdown timer.mp4";

Notes:

  • We used a single black frame for the background that defined the size of the video frame as well.
  • Using the fps variable we defined the number of Frames per Second for the video.
  • The seconds variable defined the number of seconds the duration of the video should be.
  • The mantissaDigits variable defines how many decimal digits should be shown after the dot.
  • upperFont and lowerFont define the size of the fonts in the upper row and the lower one respectively.
  • We used the drawtext directive twice to write to the frames.

Notes on the first drawtext:

  • fontfile='/usr/share/fonts/urw-base35/C059-Bold.otf' defines the font to be used for the text.
  • fontcolor=yellow defines the color of the font of the text.
  • fontsize=$upperFont defines the size of the font of the text.
  • x=(w-text_w)/2 defines the X-coordinate of the location for the text on the frame, here we center the text horizontally on the frame.
  • y=(h-text_h)/2 defines the Y-coordinate of the location for the text on the frame, here we center the text vertically on the frame.
  • text='%{eif\:($seconds-t)\:d}.%{eif\:(mod($seconds-t, 1)*pow(10,$mantissaDigits))\:d\:$mantissaDigits}' We print the remaining seconds for the video to finish with specific decimal digit accuracy.

Notes on the second drawtext:

  • drawtext=fontfile='/usr/share/fonts/urw-base35/C059-Bold.otf' defines the font to be used for the text.
  • fontcolor=yellow defines the color of the font of the text.
  • fontsize=$lowerFont defines the size of the font of the text.
  • x=(w-text_w)/2 defines the X-coordinate of the location for the text on the frame, here we center the text horizontally on the frame.
  • y=((h-text_h)/2)+$upperFont defines the Y-coordinate of the location for the text on the frame, here shift the text from the vertical center  of the frame.
  • text='Elapsed\: %{eif\:(t)\:d}.%{eif\:(mod(t, 1)*pow(10,$mantissaDigits))\:d\:$mantissaDigits}' We print the elapsed seconds since the video started with specific decimal digit accuracy.

Perform diff on two folders

To perform a recursive diff on all the files of two folders we just need to add the -r (or --recursive) parameter that recursively compares any subdirectories found.

To avoid needless messages from the tool, we can also use the -q (or --brief) parameter that reports only when files differ.

Example of performing diff on two folders recursively while preventing needless messages.

diff -rq aFolder someOtherFolder;


How to find lines that contain only lowercase characters

To print all lines that contain only lower case characters, we used the following regular expression in grep:

egrep '^[[:lower:]]+$' <file>;
#If you do not have egrep, use
grep -e '^[[:lower:]]+$' <file>;

Breakdown of the above regular expression:

  • ^ instructs the regular expression parser that the pattern should always start with the beginning of the line
  • [[:lower:]] this special instruction informs us that only lower case characters can match it
  • + the plus sign causes the preceding token to be matched one or more times
  • $ signifies the end of the line

Fedora 26: C++: static linking cannot find -lstdc++ -lm and -lc

Recently, we were trying to compile a C++ application with the following compilation command on a Fedora 26 64bit :

g++ -static -O2 -lm -Wall -Wno-unused-result -std=c++14 -DCS_ACADEMY -DONLINE_JUDGE 510152025.cpp -o 510152025;

unfortunately, we got the following errors:

 /usr/bin/ld: cannot find -lstdc++
 /usr/bin/ld: cannot find -lm
 /usr/bin/ld: cannot find -lc
 collect2: error: ld returned 1 exit status

To resolve the issues, we performed the following installations to install the static versions of the glibc and libstdc libraries:

sudo dnf install glibc-static libstdc++-static -y;

 


Bash: After redirected input file is done, allow user to control application via STDIN

Recently, we needed to start an application using a script, which application had its own CLI.
After starting it, we had to feed it with some input to configure it before handing it over to the end user.

The application we used was named dog. We wrote into a plain text file (named food) the commands that we needed to feed the application and then we started the execution using a plain input redirect like so dog < food;.
Doing so, resulted into properly feeding the dog application the food data  but it would cause the application to terminate immediately after the food was done as it would also feed the EOF (End Of File) directive to dog.
Apparently, the application after receiving the EOF, it would then terminate without allowing the end user to take control.

To mitigate the problem, we used the cat command to concatenate the input file along with the stdin stream, permitting us to first feed all the data in the food file and then allow the user to input data using the standard input stream.
Our final command resulted in the following solution as below

cat <(cat food) - | dog;

Where - is the special sign for standard input stdin.
cat food can be of course replaced with other commands that produce output on the standard output (stdout).

A bad side-effect of this solution, is that we lost some functionality of the terminal including, but not limited to, using the backspace and navigation.


Find all git repositories and perform a pull operation on them.

The following command will find all git projects in your home folder and perform a pull operation on them.

find ~ -name ".git" -type d -exec bash -c "echo '{}' && cd '{}'/.. && git pull" \;

The above command is based on finding the .git folders that exist in any clone of a git repository. Once a .git folder is found, it will navigate to its parent folder where it will perform the pull request.

Bonus – Automate the procedure using a cron job

The following entry in crontab allows us to periodically perform a pull request on all the cloned repositories that we have in a specific folder. Specifically, it will perform this operation once every five minutes.

*/5    *    *    *    *    cd /home/bytefreaks/Projects; find . -name ".git" -type d -exec bash -c "echo '{}' && cd '{}'/.. && git pull" \; &> /tmp/bf.git.log

Please note that it would be easier to use an ssh key that does not have a password for this automation.
If you do not, the you will need to either pass the password via this configuration line (not recommended) or have a key agent running to provide the password for the key.