In Linux, the find command is most commonly used to search files using different criteria such as file name, size and modified time. Did you know that you can search files using inode number as well? Here is how to do it?

With “ls” we can find the inode number –

$ ls -li /etc/hosts
1576843 -rw-r--r-- 1 root root 311 Jan 21  2017 /etc/hosts

Using “-inum” option of find command, we can locate the filename and its path by its inode number.

$ find /etc -type f -inum 1576843 2>/dev/null 
/etc/hosts

$ cat $(find /etc -type f -inum 1576843 2>/dev/null)
127.0.0.1	localhost
127.0.1.1	ubuntu

References

http://man7.org/linux/man-pages/man7/inode.7.html

http://man7.org/linux/man-pages/man1/find.1.html

Linux – show file system type

How to print the file system type of a mount


Linux supports several file systems, including VFAT, ext2, ext3, ext4 and Reiser. The ext* family of file systems are probably the most popular ones.

The quickest way to view the file system on which each FILE resides, or all file systems is the “df” command.

$ df -Th
Filesystem     Type      Size  Used Avail Use% Mounted on
udev           devtmpfs  481M  4.0K  481M   1% /dev
tmpfs          tmpfs      99M  1.2M   98M   2% /run
/dev/sda1      ext4       46G   32G   12G  73% /
none           tmpfs     4.0K     0  4.0K   0% /sys/fs/cgroup
none           tmpfs     5.0M     0  5.0M   0% /run/lock
none           tmpfs     494M   12K  494M   1% /run/shm
none           tmpfs     100M   36K  100M   1% /run/user
In the above example, with "df -Th", we can see the file system type
("-T" option) in a human readable ("-h") size format.

Reference

http://linuxcommand.org/lc3_man_pages/df1.html

There are several tools for compressing and decompressing files in Linux, you can get a summary of these tools in this link. Zip is one of the utilities used for packaging, compressing (archive) and decompressing files.

Installation

  • Ubuntu
sudo apt-get update
sudo apt-get install zip unzip
  • RedHat or CentOS
sudo yum install unzip

Compress files

Compress files in a directory named tutorial –

$ zip -r tutorial.zip tutorial/
   adding: tutorial/ (stored 0%)
   adding: tutorial/host.conf (deflated 13%)
   adding: tutorial/hostname (stored 0%)
   adding: tutorial/hosts.deny (deflated 44%)
   adding: tutorial/hosts (deflated 35%)
   adding: tutorial/hosts.allow (deflated 42%)
   adding: tutorial/auth_sa.py (deflated 52%)

View contents of zip files, without uncompressing –

$ zip -sf tutorial
 Archive contains:
   tutorial/
   tutorial/host.conf
   tutorial/hostname
   tutorial/hosts.deny
   tutorial/hosts
   tutorial/hosts.allow
   tutorial/auth_sa.py
 Total 7 entries (2487 bytes)

Unzip or decompress

To decompress a zipped file, use the unzip command –

 $ unzip tutorial.zip
Archive:  tutorial.zip
   creating: tutorial/
  inflating: tutorial/host.conf
 extracting: tutorial/hostname
  inflating: tutorial/hosts.deny
  inflating: tutorial/hosts
  inflating: tutorial/hosts.allow
  inflating: tutorial/auth_sa.py

Search and compress

You can also combine find and zip command to search for certain types of files and compress those files in one command –

 $ find . -type f -name '*.conf' -print | zip confi-files -@
  adding: host.conf (deflated 13%)
  adding: colord.conf (deflated 50%)
  adding: ntp.conf (deflated 56%)

$ zip -sf confi-files
Archive contains:
  host.conf
  colord.conf
  ntp.conf
Total 3 entries (1858 bytes)
References -
https://linux.die.net/man/1/zip

Contents of most text files change during the life of the file , and it is common to find yourself trying to search and replace certain text across multiple files. In Linux, this is a fairly easy task. Let us go through some of the commands you will need to perform this task and then finally construct a single liner to do the job.

  • grep is your best friend when it comes to finding a string in a file. In this case we are looking for the string “REPLACEME” in current directory and across multiple files –
$ grep -r REPLACEME *
host.conf:# The "REPLACEME" line is only used by old versions of the C library.
host.conf:order hosts,REPLACEME,bind
hostname:REPLACEME
hosts.deny:ALL: REPLACEME

If we are interested only in the files which contains this particular text –

$ grep -lr REPLACEME *
host.conf
hostname
hosts.deny
  • sed is a tool of choice for inline editing of files –
$ cat data 
This text will be replaced - REPLACEME
$ sed -i 's/REPLACEME/NEWTEXT/g' data 
$ cat data 
This text will be replaced - NEWTEXT

From here, there are multiple ways to skin the cat – we can loop through the files and do the replacement or we can let the commands do the replacement with a wildcard.

For loop style update -

$ for f in $(grep -lr REPLACEME *); do echo "*** File: ${f} ***" ; sed -i 's/REPLACEME/NEWTEXT/g' $f; done
*** File: host.conf ***
*** File: hostname ***
*** File: hosts.deny ***

$ grep -lr REPLACEME *

$ grep -lr NEWTEXT *
data
host.conf
hostname
hosts.deny

Actually the above for loop is redundant, sed can make changes across multiple files –

 sed -i 's/REPLACEME/NEWTEXT/g' *

How to install Google cloud platform(GCP) sdk – gcloud cli tool


The instructions below were testing in Ubuntu Linux.

gcloud is the command line interface(CLI) tool for interacting with GCP services. Per Google’s product overview page for gcloud – “The Cloud SDK is a set of tools for Cloud Platform. It contains gcloud, gsutil, and bq, which you can use to access Google Compute Engine, Google Cloud Storage, Google BigQuery, and other products and services from the command-line. You can run these tools interactively or in your automated scripts”.

Let us download, install and initialize this tool in an interactive manner, accept all default settings for all prompts –

$ curl https://sdk.cloud.google.com | bash && exec -l $SHELL
$ gcloud init
If above installation steps go well, check its version –
$ gcloud version
Google Cloud SDK 224.0.0
bq 2.0.36
core 2018.11.02
gsutil 4.34
 
A simple way to validate if the CLI is working as expected is to list all the GCP regions –
$ gcloud compute regions list
NAME                     CPUS  DISKS_GB  ADDRESSES  RESERVED_ADDRESSES  STATUS  TURNDOWN_DATE
asia-east1               0/8   0/2048    0/8        0/1                 UP
asia-east2               0/8   0/2048    0/8        0/1                 UP
asia-northeast1          0/8   0/2048    0/8        0/1                 UP
asia-south1              0/8   0/2048    0/8        0/1                 UP
asia-southeast1          0/8   0/2048    0/8        0/1                 UP
australia-southeast1     0/8   0/2048    0/8        0/1                 UP
europe-north1            0/8   0/2048    0/8        0/1                 UP
europe-west1             0/8   0/2048    0/8        0/1                 UP
europe-west2             0/8   0/2048    0/8        0/1                 UP
europe-west3             0/8   0/2048    0/8        0/1                 UP
europe-west4             0/8   0/2048    0/8        0/1                 UP
northamerica-northeast1  0/8   0/2048    0/8        0/1                 UP
southamerica-east1       0/8   0/2048    0/8        0/1                 UP
us-central1              0/8   0/2048    0/8        0/1                 UP
us-east1                 2/8   31/2048   2/8        0/1                 UP
us-east4                 0/8   0/2048    0/8        0/1                 UP
us-west1                 0/8   0/2048    0/8        0/1                 UP
us-west2                 0/8   0/2048    0/8        0/1                 UP

Only the core components of the gcloud sdk are installed during initial installation. For any additional component to interact with GCP, you have to install the additional component. For instance, to install the component for interactive with Google Kubernetes Engine(GKE) you have to install kubectl


gcloud components install kubectl

Many features of GCP are available in Beta only, for that you have to install the beta component –


gcloud components install beta

Stay up to date with  –

gcloud components update 

.

Tab completion and running commands in Beta feature –


$ gcloud beta container  [tab][tab]
binauthz  clusters  get-server-config  images  node-pools  operations  subnets

$ gcloud beta container get-server-config
Fetching server config for us-east1-c
defaultClusterVersion: 1.9.7-gke.7
defaultImageType: COS
validImageTypes:
- COS
- UBUNTU
- COS_CONTAINERD
validMasterVersions:
- 1.11.2-gke.15
- 1.10.9-gke.3
- 1.10.7-gke.9
- 1.10.6-gke.9
- 1.9.7-gke.7
validNodeVersions:
- 1.11.2-gke.15
- 1.11.2-gke.9
- 1.10.9-gke.3
- 1.10.9-gke.0
- 1.10.7-gke.9
- 1.10.7-gke.6
- 1.10.7-gke.2
- 1.10.7-gke.1
- 1.10.6-gke.9
- 1.10.6-gke.6
- 1.10.6-gke.4
- 1.10.6-gke.3
- 1.10.6-gke.2
- 1.10.6-gke.1
- 1.10.5-gke.4
- 1.10.5-gke.3
- 1.10.5-gke.2
- 1.10.5-gke.0
- 1.10.4-gke.3
- 1.10.4-gke.2
- 1.10.4-gke.0
- 1.10.2-gke.4
- 1.10.2-gke.3
- 1.10.2-gke.1
- 1.9.7-gke.7
- 1.9.7-gke.6
- 1.9.7-gke.5
- 1.9.7-gke.4
- 1.9.7-gke.3
- 1.9.7-gke.1
- 1.9.7-gke.0
- 1.9.6-gke.2
- 1.9.6-gke.1
- 1.9.3-gke.0
- 1.8.12-gke.3
- 1.8.12-gke.2
- 1.8.12-gke.1
- 1.8.12-gke.0
- 1.8.10-gke.2
- 1.8.10-gke.0
- 1.8.9-gke.1
- 1.8.8-gke.0
- 1.7.15-gke.0
- 1.7.12-gke.2
- 1.6.13-gke.1

Reference –

Installation – https://cloud.google.com/sdk/docs/downloads-interactive#linux

SDK Components – https://cloud.google.com/sdk/docs/components

Tips and Tricks – https://cloudplatform.googleblog.com/2014/03/tips-and-tricks-command-line-access-to.html

Ansible : How to run playbooks as a shell script


Ansible is a powerful tool for automation, its syntax checking, verbose and dry run mode features make it a reliable and safe tool. It is particularly popular in IT infrastructure automation, such as application deployment or full fledged infrastructure plus app deployment. As an integral part of DevOps tool-set, it falls into the category of Chef, Puppet, Salt or CFEngine for the critical role it plays in IT infrastructure, Application Deployment, Configuration Management and Continuous Delivery.

In this short blog, I am writing about a little known or less popular usage of Ansible – executing it like a shell script. In a Unix-like operating system, any text file with its content starting with a #! aka Shebang, is executed by passing the text file as an argument to the characters following the Shebang. For instance, a text file /tmp/myscript.sh with its content starting with the characters #!/bin/bash is run by the program loader as /bin/bash /tmp/myscript. Following the same logic, we can execute any ansible playbook by simply starting the content of the playbook file with a path to the ansible executable. 

Thus for me to execute my playbooks just like a script, the first thing I need to know is the path to my Ansible executable –

$ which ansible
/usr/local/bin/ansible

And have a playbook – in this case, I will use two playbook – one which adds a user and the second one which deletes the same user as examples.
Notice that I am naming the playbook just like a shell script and made it executable –

$ cat add-user.sh 
#!/usr/local/bin/ansible-playbook
---
- hosts: localhost
  tasks:
  - name: Add user
    user: name={{ username }} comment={{ comment }} state=present shell={{ shell }}
    become: yes

When I execute this script, I will pass the parameters needed to add a user as ansible Extra variables. Now let us run the script in dry run mode first –

$ id john
id: ‘john’: no such user

$ ./add-user.sh -e "username=john comment='John Doe' shell=/bin/bash" -v --check
Using /etc/ansible/ansible.cfg as config file

PLAY [localhost] ************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************
ok: [localhost]

TASK [Add user] *************************************************************************************************************************************
changed: [localhost] => {"changed": true}

PLAY RECAP ******************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0

Everything looks good, so let us execute it –

$ ./add-user.sh -e "username=john comment='John Doe' shell=/bin/bash" -v
Using /etc/ansible/ansible.cfg as config file

PLAY [localhost] ************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************
ok: [localhost]

TASK [Add user] *************************************************************************************************************************************
changed: [localhost] => {"changed": true, "comment": "John Doe", "create_home": true, "group": 1002, "home": "/home/john", "name": "john", "shell": "/bin/bash", "state": "present", "stderr": "useradd: warning: the home directory already exists.\nNot copying any file from skel directory into it.\n", "stderr_lines": ["useradd: warning: the home directory already exists.", "Not copying any file from skel directory into it."], "system": false, "uid": 1002}

PLAY RECAP ******************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0

$ id john
uid=1002(john) gid=1002(john) groups=1002(john)

Deleting the user is similar, we just write an equivalent playbook and we pass only the username name as an extra var this time –

$ cat del-user.sh
#!/usr/local/bin/ansible-playbook
---
- hosts: localhost
tasks:
- name: Delete user
user: name={{ username }} state=absent
become: yes

$ ./del-user.sh -e username=john -v --check
Using /etc/ansible/ansible.cfg as config file

PLAY [localhost] ************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************
ok: [localhost]

TASK [Delete user] **********************************************************************************************************************************
changed: [localhost] => {"changed": true}

PLAY RECAP ******************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0

$ ./del-user.sh -e username=john -v
Using /etc/ansible/ansible.cfg as config file

PLAY [localhost] ************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************
ok: [localhost]

TASK [Delete user] **********************************************************************************************************************************
changed: [localhost] => {"changed": true, "force": false, "name": "john", "remove": false, "state": "absent"}

PLAY RECAP ******************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0

$ id john
id: ‘john’: no such user

You can find more on Ansible in the documentation section of the official site.