LFCS Part 49: Command Line Completion and bash-completion

Master Tab completion in Linux to dramatically speed up your command-line workflow with automatic command, file, and argument completion

18 min read

Introduction

Imagine typing long file paths, complex commands, or remembering exact option names every single time you use the terminal. It would be tedious, error-prone, and incredibly slow. Fortunately, bash provides one of the most powerful productivity features in the Linux terminal: Tab completion.

Tab completion automatically completes what you're typing—whether it's a command name, file path, directory name, or even command-specific arguments. A single press of the Tab key can save you dozens of keystrokes and eliminate typos. It's one of those features that once you master it, you'll wonder how you ever worked without it.

In this comprehensive guide, you'll learn everything about Tab completion and the bash-completion package. By the end, you'll be able to leverage completion for commands, files, variables, and even install advanced completion that understands specific command syntax.

What is Tab Completion?

Tab completion is bash's intelligent auto-completion system. When you press the Tab key while typing a command, file path, or argument, bash attempts to complete what you're typing based on available options.

How it works:

  1. You start typing something
  2. Press Tab
  3. Bash looks at what you've typed
  4. If there's only one match, bash completes it automatically
  5. If there are multiple matches, bash either shows options or completes as much as possible

Why it matters:

  • Speed: Type less, work faster
  • Accuracy: Avoid typos in long filenames or paths
  • Discovery: Find available commands and options without documentation
  • Productivity: One of the most-used features by experienced Linux users

Basic Tab Completion

Single Tab Press

When you press Tab once, bash tries to complete what you're typing.

Example 1: Complete a command

# Type "sys" and press Tab
sys[Tab]
# Bash completes to:
systemctl

If only one command starts with "sys", bash completes it automatically.

Example 2: Complete a filename

# Type "ls Doc" and press Tab
ls Doc[Tab]
# Bash completes to:
ls Documents/

Example 3: Complete a path

# Type "cd /e" and press Tab
cd /e[Tab]
# Bash completes to:
cd /etc/

Double Tab Press

When you press Tab twice, bash shows you all possible completions if there are multiple matches.

Example: Multiple matches

# Type "user" and press Tab twice
user[Tab][Tab]
useradd  userdel  usermod  users

Bash displays all commands starting with "user".

Another example: See all files

# In a directory with multiple files starting with "test"
ls test[Tab][Tab]
test1.txt  test2.txt  test_backup.txt  testing.log

Partial Completion

If multiple matches share a common prefix, bash completes up to the point where they differ.

Example:

# You have: file1.txt, file2.txt, file_backup.txt
ls f[Tab]
# Bash completes to:
ls file
# Then press Tab twice to see options:
ls file[Tab][Tab]
file1.txt  file2.txt  file_backup.txt

# Type "1" and Tab again:
ls file1[Tab]
# Completes to:
ls file1.txt

Command Name Completion

Bash can complete command names based on what's in your PATH.

Complete Full Commands

# Type "systemc" and press Tab
systemc[Tab]
systemctl

# Type "ip" and press Tab twice
ip[Tab][Tab]
ip         ip6tables  ipcalc     ipcrm      ipcs

Discover Available Commands

Use Tab completion to explore what commands are available:

# See all commands starting with "net"
net[Tab][Tab]
netstat  netcat  network-manager  networkctl

# See all commands starting with "sys"
sys[Tab][Tab]
sysctl  systemctl  systemd  system-config-printer

This is incredibly useful when you can't remember the exact command name.

Case Sensitivity

Tab completion is case-sensitive by default:

# This works:
Desk[Tab]
Desktop/

# This doesn't work (no completion):
desk[Tab]
# (no match if the directory is "Desktop")

File and Directory Completion

Tab completion shines when working with files and directories.

Complete File Names

# List a specific file
ls -l do[Tab]
ls -l document.txt

# Edit a configuration file
vim /etc/ss[Tab]
vim /etc/ssh/
# Press Tab again:
vim /etc/ssh/sshd_[Tab]
vim /etc/ssh/sshd_config

Complete Directory Paths

# Navigate to a directory
cd /v[Tab]
cd /var/

# Continue the path:
cd /var/l[Tab]
cd /var/log/

# Even deeper:
cd /var/log/au[Tab]
cd /var/log/audit/

Work with Long File Names

Tab completion is essential for long or complex filenames:

# Create a file with a very long name
touch very_long_filename_that_would_be_annoying_to_type.txt

# Use it with Tab completion:
cat v[Tab]
cat very_long_filename_that_would_be_annoying_to_type.txt

From the lfcs-rough.txt example:

touch kfnkjsdnfkjsnalfklsfnkjsdnkjfnksjdnfgsnzkjfdgfnkernjkzndskl

# Later, just type:
ls -l kfn[Tab]
ls -l kfnkjsdnfkjsnalfklsfnkjsdnkjfnksjdnfgsnzkjfdgfnkernjkzndskl

You save yourself from typing 60+ characters!

Hidden Files Completion

Tab completion works with hidden files (those starting with .):

# In your home directory
cat .[Tab][Tab]
.bash_history  .bash_logout  .bash_profile  .bashrc

# Complete a specific hidden file:
cat .bash_h[Tab]
cat .bash_history

Variable Name Completion

You can use Tab completion with shell variables when using $.

Complete Variable Names

# Type "echo $HIS" and press Tab twice
echo $HIS[Tab][Tab]
$HISTCMD  $HISTCONTROL  $HISTFILE  $HISTFILESIZE  $HISTSIZE

# Complete a specific one:
echo $HISTF[Tab]
echo $HISTFILE

From the lfcs-rough.txt example:

echo $HIST[Tab][Tab]
$HISTCMD       $HISTCONTROL   $HISTFILE      $HISTFILESIZE  $HISTSIZE

echo $HISTFILESIZE
1000

Common Variables to Explore

# See all PATH-related variables
echo $PATH[Tab][Tab]

# See all HOME-related variables
echo $HOME[Tab][Tab]

# See all SHELL-related variables
echo $SHELL[Tab][Tab]

Limitations of Basic Tab Completion

Basic Tab completion has some limitations:

1. Command-Specific Arguments Not Completed

Basic bash completion doesn't understand command-specific options:

# This doesn't autocomplete systemctl service names:
systemctl restart [Tab][Tab]
# (shows all files in current directory, not services)

# This doesn't autocomplete Git branches:
git checkout [Tab][Tab]
# (shows files, not branches)

2. No Context-Aware Completion

# ssh completion doesn't know about hosts
ssh user@[Tab]
# (no completion)

# apt doesn't complete package names (without bash-completion)
sudo apt install vim[Tab]
# (no smart completion)

This is where bash-completion package comes in.

Installing bash-completion

The bash-completion package provides intelligent, context-aware completion for hundreds of commands.

Check if Already Installed

# Check if bash-completion is installed
dpkg -l | grep bash-completion     # Debian/Ubuntu
rpm -qa | grep bash-completion     # RHEL/CentOS

# Or check if the directory exists:
ls /usr/share/bash-completion/

Install on Different Distributions

Ubuntu/Debian:

sudo apt update
sudo apt install bash-completion

CentOS/RHEL/Rocky Linux:

sudo dnf install bash-completion
# Or on older systems:
sudo yum install bash-completion

Fedora:

sudo dnf install bash-completion

Arch Linux:

sudo pacman -S bash-completion

Enable bash-completion

After installation, you may need to enable it:

For system-wide use, it's usually enabled by default. Check /etc/profile.d/bash_completion.sh exists:

ls -l /etc/profile.d/bash_completion.sh

For your user, ensure it's loaded in ~/.bashrc:

# Add to ~/.bashrc if not present:
if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
fi

Apply changes:

source ~/.bashrc
# Or log out and log back in

Verify It's Working

# Try systemctl service completion:
systemctl restart [Tab][Tab]
# Should show available services

# Try Git branch completion:
git checkout [Tab][Tab]
# Should show branches (if in a git repo)

Advanced Completions with bash-completion

With bash-completion installed, you get intelligent completion for many commands.

systemctl Completion

Complete service names:

systemctl status [Tab][Tab]
nginx.service  sshd.service  httpd.service  firewalld.service

systemctl restart ssh[Tab]
systemctl restart sshd.service

Complete systemctl commands:

systemctl [Tab][Tab]
start     stop      restart   reload    enable    disable   status    is-active

SSH Completion

Complete hostnames from ~/.ssh/known_hosts and /etc/hosts:

ssh [Tab][Tab]
user@192.168.1.100  user@server1  user@webserver

ssh user@web[Tab]
ssh user@webserver

Git Completion

Complete Git commands:

git [Tab][Tab]
add      branch   checkout   commit   diff   log   merge   pull   push   status

git che[Tab]
git checkout

Complete branch names:

git checkout [Tab][Tab]
main  develop  feature/new-login  bugfix/issue-123

git checkout fea[Tab]
git checkout feature/new-login

Complete remote names:

git push [Tab][Tab]
origin  upstream

git push ori[Tab]
git push origin

apt/dnf Package Completion

Complete package names:

# Ubuntu/Debian
sudo apt install vim-[Tab][Tab]
vim-common  vim-runtime  vim-gtk3  vim-nox

# RHEL/CentOS
sudo dnf install vim-[Tab][Tab]
vim-common  vim-enhanced  vim-minimal

Docker Completion

Complete Docker commands:

docker [Tab][Tab]
build   exec    images  logs    ps      pull    run     start   stop

docker ex[Tab]
docker exec

Complete container names:

docker exec -it [Tab][Tab]
webserver  database  redis_cache

docker exec -it web[Tab]
docker exec -it webserver

make Completion

Complete Makefile targets:

# In a directory with a Makefile
make [Tab][Tab]
all   build   clean   install   test

make c[Tab]
make clean

Man Page Completion

Complete man page names:

man sys[Tab][Tab]
sysctl  systemctl  systemd  system

man systemc[Tab]
man systemctl

User and Group Completion

Complete usernames:

su - [Tab][Tab]
root  user1  centos9  admin

chown [Tab][Tab]
root  user1  centos9  admin

Complete group names:

chgrp [Tab][Tab]
root  wheel  users  docker

How bash-completion Works

bash-completion works through completion scripts stored in specific directories:

Completion Script Locations

System-wide completions:

/usr/share/bash-completion/completions/

View available completions:

ls /usr/share/bash-completion/completions/ | head -20
git
ssh
systemctl
docker
make
apt
dnf
tar
rsync

Each file is a completion script for that specific command.

Completion Functions

Completion scripts define bash functions that tell bash how to complete arguments for specific commands.

Example: View the systemctl completion script:

cat /usr/share/bash-completion/completions/systemctl | head -20

These scripts use bash's complete built-in to register completion behavior.

Customizing Tab Completion

You can customize Tab completion behavior through bash options and readline settings.

Case-Insensitive Completion

Make Tab completion case-insensitive:

Add to ~/.inputrc:

set completion-ignore-case on

Apply changes:

bind -f ~/.inputrc
# Or start a new shell

Now you can type:

cd desk[Tab]
cd Desktop/   # Works even though you typed lowercase

Show All Completions Immediately

Instead of beeping when multiple matches exist, show them immediately:

Add to ~/.inputrc:

set show-all-if-ambiguous on

Now a single Tab press shows all matches instead of requiring two Tab presses.

Colored Completion

Show completion matches with colors (like ls --color):

Add to ~/.inputrc:

set colored-stats on

Visible Bell Instead of Audible

If Tab completion can't find a match, show a visual flash instead of beeping:

Add to ~/.inputrc:

set bell-style visible

Complete Command Suggestions

Show suggestions as you type (like fish shell):

Add to ~/.inputrc:

set show-all-if-unmodified on
set menu-complete-display-prefix on

Creating Custom Completion Scripts

You can create your own completion scripts for custom commands or scripts.

Basic Completion Example

Create a completion for a custom script that takes specific arguments:

# Create a simple completion function
_my_script_completion() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -W "start stop restart status" -- $cur) )
}

# Register the completion
complete -F _my_script_completion my_script

Now when you type:

my_script [Tab][Tab]
start  stop  restart  status

Save Custom Completions

Create a file: ~/.bash_completion.d/my_completions

mkdir -p ~/.bash_completion.d

cat > ~/.bash_completion.d/my_completions << 'EOF'
# Custom completion for my_script
_my_script_completion() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -W "start stop restart status" -- $cur) )
}
complete -F _my_script_completion my_script
EOF

Load it in ~/.bashrc:

# Add to ~/.bashrc
if [ -d ~/.bash_completion.d ]; then
    for file in ~/.bash_completion.d/*; do
        [ -r "$file" ] && source "$file"
    done
fi

Apply:

source ~/.bashrc

File-Based Completion

Create completion that suggests files with specific extensions:

# Complete only .conf files
_myconfig_completion() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -f -X '!*.conf' -- $cur) )
}
complete -F _myconfig_completion myconfig

Practical Tab Completion Tips

Tip 1: Complete Paths Incrementally

Build paths piece by piece:

cd /v[Tab]/l[Tab]/au[Tab]
# Expands to:
cd /var/log/audit/

Tip 2: Use Tab to Verify Files Exist

Before running a command, use Tab to verify the file exists:

cat important_fi[Tab]
# If it doesn't complete, the file doesn't exist

Tip 3: Discover Command Options

Even without bash-completion, you can discover files and directories:

ls /etc/sys[Tab][Tab]
sysconfig/  sysctl.d/  systemd/

Tip 4: Escape Special Characters

Tab completion automatically escapes special characters:

# File name: "my file with spaces.txt"
cat my[Tab]
cat my\ file\ with\ spaces.txt

Tip 5: Complete Multiple Arguments

Use Tab for each argument:

cp /etc/hos[Tab] /tmp/hos[Tab]
cp /etc/hosts /tmp/hosts.backup

Tip 6: Use Tab with Wildcards

Combine Tab with wildcards:

ls *.tx[Tab]
# Completes to *.txt if that's the only match

Common Tab Completion Shortcuts

| Keys | Action | |------|--------| | Tab | Complete or show matches (once) | | Tab Tab | Show all matches | | Esc Tab | Same as Tab Tab (alternative) | | Alt-? | Show possible completions | | Alt-* | Insert all possible completions | | Ctrl-x / | List possible filename completions |

Best Practices for Tab Completion

1. Always Use Tab for Long Paths

Never type full paths when you can use Tab:

# Bad (tedious):
vim /etc/systemd/system/multi-user.target.wants/sshd.service

# Good (fast):
vim /e[Tab]/sys[Tab]/sys[Tab]/m[Tab]/ss[Tab]

2. Double-Tab to Explore

When unsure what's available, double-Tab to explore:

systemctl [Tab][Tab]
# See all systemctl subcommands

ls /var/[Tab][Tab]
# See all directories in /var

3. Install bash-completion on Servers

Always install bash-completion on servers you manage:

sudo dnf install bash-completion

It dramatically improves productivity.

4. Learn Your Most-Used Completions

Focus on completing:

  • Commands you frequently type
  • Long file paths
  • Service names
  • Package names
  • Git branches

5. Combine with History

Use Tab completion with history expansion:

!vim[Tab]
# Completes to your last vim command
vim /etc/ssh/sshd_config

Troubleshooting Tab Completion

Problem 1: Tab Completion Not Working

Symptoms: Tab key just inserts a tab character or does nothing.

Solutions:

# 1. Check if bash-completion is installed
dpkg -l | grep bash-completion    # Debian/Ubuntu
rpm -qa | grep bash-completion    # RHEL/CentOS

# 2. Install it if missing
sudo apt install bash-completion  # Debian/Ubuntu
sudo dnf install bash-completion  # RHEL/CentOS

# 3. Reload your shell
source ~/.bashrc

Problem 2: Completion Shows Wrong Results

Symptoms: Completion suggests files instead of command-specific options.

Cause: bash-completion not installed or not loaded.

Solution:

# Verify bash-completion is loaded
type _init_completion
# Should output: _init_completion is a function

# If not, add to ~/.bashrc:
if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
fi

source ~/.bashrc

Problem 3: Slow Tab Completion

Symptoms: Tab completion takes several seconds.

Causes: Network-mounted filesystems, slow disk, or complex completion scripts.

Solutions:

# 1. Disable completion for slow commands
complete -r slow_command

# 2. Reduce bash-completion scope
# Edit ~/.bashrc and comment out slow completions

Problem 4: Case-Sensitive Issues

Symptoms: Tab completion doesn't work with different case.

Solution: Enable case-insensitive completion in ~/.inputrc:

set completion-ignore-case on

Tab Completion Command Reference

| Command/Setting | Description | |----------------|-------------| | Tab | Attempt to complete current word | | Tab Tab | Show all possible completions | | complete -p | Show all completion settings | | complete -r command | Remove completion for command | | compgen -c | List all available commands | | compgen -d | List directories | | compgen -f | List files | | compgen -v | List variables | | bind -p | Show all readline keybindings |

Practice Labs

Let's practice Tab completion with hands-on exercises.

Lab 1: Basic Command Completion

Task: Type "sys" and use Tab completion to complete to "systemctl".

Solution
sys[Tab]
# Should complete to:
systemctl

Lab 2: Double Tab to See Options

Task: Type "user" and press Tab twice to see all commands starting with "user".

Solution
user[Tab][Tab]
# Shows:
useradd  userdel  usermod  users

Lab 3: Complete File Names

Task: Create a file called "test_completion.txt", then use Tab to complete "cat test" to view it.

Solution
touch test_completion.txt
cat test_c[Tab]
cat test_completion.txt

Lab 4: Complete Directory Paths

Task: Use Tab completion to navigate to /var/log.

Solution
cd /v[Tab]/l[Tab]
cd /var/log/

Lab 5: Complete Hidden Files

Task: In your home directory, use Tab to complete and view your .bash_history file.

Solution
cat ~/.bash_h[Tab]
cat ~/.bash_history

Lab 6: Variable Name Completion

Task: Use Tab completion to see all variables starting with "HIST".

Solution
echo $HIST[Tab][Tab]
# Shows:
$HISTCMD  $HISTCONTROL  $HISTFILE  $HISTFILESIZE  $HISTSIZE

Lab 7: Long File Name Completion

Task: Create a file with a very long name and use Tab to complete it when removing it.

Solution
touch this_is_a_very_long_filename_for_testing_tab_completion.txt
rm thi[Tab]
rm this_is_a_very_long_filename_for_testing_tab_completion.txt

Lab 8: Check if bash-completion is Installed

Task: Check whether bash-completion is installed on your system.

Solution
# For Debian/Ubuntu:
dpkg -l | grep bash-completion

# For RHEL/CentOS:
rpm -qa | grep bash-completion

# Or check the directory:
ls /usr/share/bash-completion/

Lab 9: Install bash-completion

Task: If bash-completion is not installed, install it.

Solution
# Ubuntu/Debian:
sudo apt update
sudo apt install bash-completion

# CentOS/RHEL:
sudo dnf install bash-completion

# Reload bash:
source ~/.bashrc

Lab 10: Test systemctl Completion

Task: If bash-completion is installed, use Tab to see available services when using systemctl restart.

Solution
systemctl restart [Tab][Tab]
# Should show available services like:
# sshd.service  nginx.service  httpd.service  ...

Lab 11: Complete systemctl Commands

Task: Type systemctl followed by a space, then press Tab twice to see available systemctl commands.

Solution
systemctl [Tab][Tab]
# Shows:
# start  stop  restart  reload  enable  disable  status  ...

Lab 12: Explore /etc Directory

Task: Use Tab completion to see what directories are in /etc that start with "sys".

Solution
ls /etc/sys[Tab][Tab]
# Shows:
# sysconfig/  sysctl.d/  systemd/

Lab 13: Complete Multiple Arguments

Task: Use Tab completion to copy /etc/hosts to /tmp/hosts.backup.

Solution
cp /etc/hos[Tab] /tmp/hos[Tab]
# Type "t.b" and Tab:
cp /etc/hosts /tmp/hosts.backup

Lab 14: Enable Case-Insensitive Completion

Task: Enable case-insensitive Tab completion for your session.

Solution
# Create or edit ~/.inputrc
echo "set completion-ignore-case on" >> ~/.inputrc

# Apply changes:
bind -f ~/.inputrc

# Test it:
cd desk[Tab]
# Should complete to Desktop/ even with lowercase

Lab 15: View All Completion Settings

Task: Display all current Tab completion settings.

Solution
complete -p
# Shows all registered completions

# Or view completions directory:
ls /usr/share/bash-completion/completions/

Lab 16: Create a Simple Custom Completion

Task: Create a completion for a fictional command called "myapp" that completes with "start", "stop", and "restart".

Solution
# Define the completion function
_myapp_completion() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -W "start stop restart" -- $cur) )
}

# Register it
complete -F _myapp_completion myapp

# Test it:
myapp [Tab][Tab]
# Shows: start  stop  restart

Lab 17: Tab Complete with Wildcards

Task: In a directory with multiple .txt files, use Tab to complete ls *.txt.

Solution
# Create test files:
touch file1.txt file2.txt file3.txt

# Use completion:
ls *.t[Tab]
# Completes to:
ls *.txt

Lab 18: Verify Completion Scripts Exist

Task: Check which completion scripts are available for common commands.

Solution
ls /usr/share/bash-completion/completions/ | grep -E "git|ssh|systemctl|docker"
# Shows available completion scripts for these commands

Lab 19: Complete User Names

Task: Use Tab completion with the su command to see available users.

Solution
su - [Tab][Tab]
# Shows available users:
# root  user1  centos9  ...

Lab 20: Navigate Deeply Nested Directories

Task: Use Tab completion to navigate to /var/log/audit efficiently.

Solution
cd /v[Tab]/l[Tab]/au[Tab]
# Completes to:
cd /var/log/audit/

Key Takeaways

  1. Tab Completion Saves Time: Single most important productivity feature in bash

  2. Single vs Double Tab:

    • One Tab: Complete if unique match
    • Two Tabs: Show all possible matches
  3. Works For:

    • Command names
    • File and directory names
    • Paths
    • Variable names (with $)
  4. bash-completion Package: Provides intelligent, context-aware completion for hundreds of commands

  5. Installation:

    • Ubuntu/Debian: sudo apt install bash-completion
    • RHEL/CentOS: sudo dnf install bash-completion
  6. Advanced Completions (with bash-completion):

    • systemctl services
    • Git branches
    • Package names
    • SSH hosts
    • Docker containers
  7. Customization: Use ~/.inputrc for case-insensitive and other behaviors

  8. Custom Completions: Create your own with complete command

  9. Always Enabled: Tab completion is always available in bash

  10. Best Practice: Use Tab for everything—commands, files, paths, arguments

What's Next?

You now understand how to leverage Tab completion to dramatically speed up your command-line workflow. In the next post, Part 50: Understanding Shell Variables, we'll learn how to:

  • Understand what variables are in bash
  • Create and use variables
  • Distinguish between local and environment variables
  • Work with important system variables (PATH, HOME, USER, SHELL)
  • Use export to make variables available to child processes
  • View all variables with env and printenv
  • Modify your PATH to add custom directories
  • Use variables in scripts and commands

Variables are fundamental to bash scripting and system configuration. Combined with the skills you've learned so far, you'll be able to customize your shell environment and write powerful scripts. See you in the next post!

Thank you for reading!

Published on January 9, 2026

Owais

Written by Owais

I'm an AIOps Engineer with a passion for AI, Operating Systems, Cloud, and Security—sharing insights that matter in today's tech world.

I completed the UK's Eduqual Level 6 Diploma in AIOps from Al Nafi International College, a globally recognized program that's changing careers worldwide. This diploma is:

  • ✅ Available online in 17+ languages
  • ✅ Includes free student visa guidance for Master's programs in Computer Science fields across the UK, USA, Canada, and more
  • ✅ Comes with job placement support and a 90-day success plan once you land a role
  • ✅ Offers a 1-year internship experience letter while you study—all with no hidden costs

It's not just a diploma—it's a career accelerator.

👉 Start your journey today with a 7-day free trial

Related Articles

Continue exploring with these handpicked articles that complement what you just read

45 min read

LFCS Part 52: Bash Startup Files and Configuration (FINAL POST!)

Master bash startup and configuration files. Learn the difference between login and non-login shells, understand /etc/profile, ~/.bashrc, ~/.bash_profile, and when to modify each file. Complete guide with practical examples and 20 hands-on labs for LFCS certification preparation.

#linux#lfcs+6 more
Read article

More Reading

One more article you might find interesting