Bash Tips & Tricks - Advanced

Information Bash Linux

Using yes command for commands or scripts that need interactive response

If some commands or scripts need user interaction and you know that you have to enter Y each time it requires input, you can use the Yes command:

yes | command_or_script

Working with file and directories

Let's say we need to create those 4 directories:

  • dir_images
  • dir_pdf
  • dir_zip
  • dir_log

Of course one could 4x make use of the mkdir command. Or you do it in just 1 line of code:

$> mkdir -v dir_{images,pdf,zip,log}
mkdir: created directory 'dir_images'
mkdir: created directory 'dir_pdf'
mkdir: created directory 'dir_zip'
mkdir: created directory 'dir_log'

Creating several nested directories at one

Let's say we want to create these directories

  • /foo
  • /foo/bar
  • /foo/bar/zoo
  • /foo/bar/zoo/whatever

We simply could do

$> mkdir foo && mkdir foo/bar && mkdir foo/bar/zoo && mkdir foo/bar/zoo/whatever

or we save us a lot of typing by adding the -p flag which creates missing parent directories as needed

$> mkdir -p foo/bar/zoo/whatever

Now we want to replace (=move) all *.txt files to *.log files, so we run a foor loop:

$> for f in ./*.txt; do mv -v ”$file” ”${file%.*}.log”; done
renamed './file10.txt' -> './file10.log'
renamed './file1.txt' -> './file1.log'
renamed './file2.txt' -> './file2.log'
renamed './file3.txt' -> './file3.log'
renamed './file4.txt' -> './file4.log'

But when you have the prename command available, you could do this more easily by:

$> prename -v 's/.txt/.log/' *.txt
file10.txt -> file10.log
file1.txt -> file1.log
file2.txt -> file2.log
file3.txt -> file3.log
file4.txt -> file4.log

Before modifying a configuration file, I is a good practice to make a backup copy of the original one by using a basic copy command like f.e.:

$> cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.back

But repeating the whole path and appending .back to the file isn't that sexy and probably error-prone. There is a shorter, neater way to do this:

$> cp /etc/sysconfig/network-scripts/ifcfg-eth0{,.back}

Check if a file is a symbolic link

$> [[ -L /path/to/file ]] && echo "passed file is a symlink"

Read a file line by line & do something with it's line content

First let us create a file with some names in is

$> cat << EOF >> names.txt
Jane Smith
John Doe
Alice Smith
Marc Brown
EOF

In this command:

  • << EOF: Starts a here document (also referred to as a "heredoc"). It's a way to pass multiline input to a command. The here document starts with << followed by a delimiter. In our example, EOF (End Of File) is used as the delimiter, but it's really just a convention; you could use any word or phrase as long as it's the same at the beginning and the end of the here document.
  • >> names.txt: Tells the shell to append the contents of the here<c/ode> document to names.txt.
  • The lines between << EOF and the second EOF are the contents of the here document. These lines will be appended to names.txt.

Print out the names in the created file via a WHILE loop:

$> while read -r line; do echo "$line"; done < names.txt

Print out the names in the file via a FOR loop:

for name in $(cat names.txt); do echo "$name"; done

But the FOR loop seperates lines by whitespace by default. So "Jane Smith" would be one line with "Jane" and another one with "Smith".  We will have to set the Internal Field Separator (IFS) to "newline":

$> IFS=$'\n'; for name in $(cat names.txt); do echo "$name"; done

Identify Open Files

The lsof command stands for List Open Files.  It is easy to remember lsof command if you think of it as “ls + of”, where ls stands for "list", and of stands for "open files".

Simply typing lsof will provide a list of all open files belonging to all active processes:

$> lsof

COMMAND  PID       USER   FD      TYPE     DEVICE  SIZE/OFF       NODE NAME
init       1       root  cwd       DIR        8,1      4096          2 /
init       1       root  txt       REG        8,1    124704     917562 /sbin/init
init       1       root    0u      CHR        1,3       0t0       4369 /dev/null
init       1       root    1u      CHR        1,3       0t0       4369 /dev/null
init       1       root    2u      CHR        1,3       0t0       4369 /dev/null
init       1       root    3r     FIFO        0,8       0t0       6323 pipe
...

List processes which opened a specific file:

$> lsof /var/log/syslog

List opened files based on process names starting with:

$> lsof -c ssh -c init

You can list the files opened by process names starting with a string, using -c option. -c followed by the process name will list the files opened by the process starting with that processes name. You can give multiple -c switch on a single command line.

List all processes using a certain mount point

Sometime when we try to umount a directory, the system will say “Device or Resource Busy” error. So we need to find out what are all the processes using the mount point and kill those processes to umount the directory. By using lsof we can find those processes:

$> lsof /home

List all files opened by a specific user:

$> lsof -u username

When you want to list files opened by all users, expect some users you can use the ^ to exclude only the particular user as follows:

$> lsof -u ^username

List all open files by a specific process by passing the PID:

$> lsof -p 1753

Kill all processes that belong to a particular user:

$> kill -9 `lsof -t -u username`

Finding Network Connection

Network connections are (technically) also files. So we can find information about them by using lsof by adding the -i option.

$> lsof -i

List all network files in use by a specific process
You can list all the network files which are being used by a process by passing the PID (process ID):

$> lsof -i -a -p 234

or even by passing the name of the process (here: ssh)

$> lsof -i -a -c ssh

List processes which are listening on a particular port:

$> lsof -i :25