Qgelm

Bash tips for everyday at the command line

Originalartikel

Backup

<html> <p>As the default shell for many of the Linux and Unix variants, Bash includes a wide variety of underused features, so it was hard to decide what to discuss.&#160;Ultimately, I decided to focus on Bash tips that make day-to-day activities easier.</p> <p>As a consultant, I see a plurality of diverse environments and work styles. I drew on this experience to narrow the tips to four broad categories: Terminal and line tricks, navigation and files, history, and helpful commands. These categories are completely arbitrary and serve more to organize my own thoughts than as any kind of definitive classification. Many of the tips included here might subjectively fit in more than one category.</p> <p>Without further ado, here are some of the most helpful Bash tricks I have encountered.</p> <h2>Working with Bash history</h2> <p>One of the best ways to increase your productivity is to learn to use the Bash history more effectively. With that in mind, perhaps one of the most important tweaks you can make in a multi-user environment is to enable the

histappend

option to your shell. To do that, simply run the following command:</p> <pre class=„bash geshifilter-bash“>shopt -s histappend </pre> <p>This allows multiple terminal sessions to write to the history at the same time. In most environments this option is <em>not</em> enabled. That means that histories are often lost if you have more than a single Bash session open (either locally or over SSH).</p> <p>Another common task is to repeat the last command with

sudo

. For example, suppose you want to create a directory

mkdir /etc/ansible/facts.d

. Unless you are root, this command will fail. From what I have observed, most users hit the

up

arrow, scroll to the beginning of the line, and add the

sudo

command. There is an easier way. Simply run the command like this:</p> <pre class=„bash geshifilter-bash“>sudo !!</pre> <p>Bash will run

sudo

and then the entirety of the previous command. Here is exactly what it looks like when run in sequence:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„8“>[user@centos ~]$ mkdir -p /etc/ansible/facts.d<br/>mkdir: cannot create directory &#8216;/etc/ansible&#8217;: Permission denied <p>[user@centos ~]$ sudo !!<br/>sudo mkdir -p /etc/ansible/facts.d</p> </div> <p>When the <strong>

!!

</strong>&#160;is run, the full command is echoed out to the terminal so you know what was just executed.</p> <p>Similar but used much less frequently is the <strong>

!*

</strong> shortcut. This tells Bash that you want all of the <em>*arguments*</em> from the previous command to be repeated in the current command. This could be useful for a command that has a lot of arguments you want to reuse. A simple example is creating a bunch of files and then changing the permissions on them:</p> <p>[user@centos tmp]$ touch file1 file2 file3 file4<br/>[user@centos tmp]$ chmod 777 !*<br/>chmod 777 file1 file2 file3 file4</p> <p>It is handy only in a specific set of circumstances, but it may save you some keystrokes.</p> <p>Speaking of saving keystrokes, let's talk about finding commands in your history. Most users will do something like this:</p> <pre class=„bash geshifilter-bash“>history |grep &lt;some command&gt;</pre> <p>However, there is an easier way to search your history. If you press</p> <pre class=„bash geshifilter-bash“>ctrl + r</pre> <p>Bash will do a reverse search of your history. As you start typing, results will begin to appear. For example:</p> <pre class=„bash geshifilter-bash“>(reverse-i-search)`hist': shopt -s histappend</pre> <p>In the above example, I typed

hist

and it matched the

shopt

command we covered earlier. If you continue pressing

ctrl + r

, Bash will continue to search backward through all of the other matches.</p> <p>Our last trick isn't a trick as much as a helpful command you can use to count and display the most-used commands in your history.</p> <p>[user@centos tmp]$ history | awk 'BEGIN {FS=„[ \t]+|\\|“} {print $3}' | sort | uniq -c | sort -nr | head<br/>81 ssh<br/>50 sudo<br/>46 ls<br/>45 ping<br/>39 cd<br/>29 nvidia-xrun<br/>20 nmap<br/>19 export</p> <p>In this example, you can see that

ssh

is by far the most-used command in my history at the moment.</p> <h2>Navigation and file naming</h2> <div id=„more-linux-resources“ class=„embedded-callout-menu callout-float-right view view-related-content-callout view-id-related_content_callout view-display-id-article_block view-dom-id-d6a8c7b5c2305d27c8b2937640d5fefb“ readability=„1“> <p>More Linux resources</p> </div> You probably already know that if you type a command, filename, or folder name, you can hit the

tab

key once to complete the wording for you. This works if there is a single exact match. However, you might not know that if you hit

tab

twice, it will show you all of the matches based on what you have typed. For example: <p>[user@centos tmp]$ cd /lib &lt;tab&gt;&lt;tab&gt;<br/>lib/ lib64/</p> <p>This can be very useful for file system navigation. Another helpful trick is to enable

cdspell

in your shell. You can do this by issuing the

shopt -s cdspell

command. This will help correct your typos:</p> <p>[user@centos etc]$ cd /tpm<br/>/tmp<br/>[user@centos tmp]$ cd /ect<br/>/etc</p> <p>It's not perfect, but every little bit helps!</p> <p>Once you have successfully changed directories, what if you need to return to your previous directory? This is not a big deal if you are not very deep into the directory tree. But if you are in a fairly deep path, such as

/var/lib/flatpak/exports/share/applications/

, you could type:</p> <pre class=„bash geshifilter-bash“>cd /va&lt;tab&gt;/lib/fla&lt;tab&gt;/ex&lt;tab&gt;/sh&lt;tab&gt;/app&lt;tab&gt;</pre> <p>Fortunately, Bash remembers your previous directory, and you can return there by simply typing

cd -

. Here is what it would look like:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„9“>[user@centos applications]$ pwd<br/>/var/lib/flatpak/exports/share/applications <p>[user@centos applications]$ cd /tmp<br/>[user@centos tmp]$ pwd<br/>/tmp</p> <p>[user@centos tmp]$ cd -<br/>/var/lib/flatpak/exports/share/applications</p> </div> <p>That's all well and good, but what if you have a bunch of directories you want to navigate within easily? Bash has you covered there as well. There is a variable you can set that will help you navigate more effectively. Here is an example:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„10“>[user@centos applications]$ export CDPATH='~:/var/log:/etc'<br/>[user@centos applications]$ cd hp<br/>/etc/hp <p>[user@centos hp]$ cd Downloads<br/>/home/user/Downloads</p> <p>[user@centos Downloads]$ cd ansible<br/>/etc/ansible</p> <p>[user@centos Downloads]$ cd journal<br/>/var/log/journal</p> </div> <p>In the above example, I set my home directory (indicated with the tilde:

~

),

/var/log

and

/etc

. Anything at the top level of these directories will be auto-filled in when you reference them. Directories that are not at the base of the directories listed in

CDPATH

will not be found. If, for example, the directory you are after was

/etc/ansible/facts.d/

this would not complete by typing

cd facts.d

. This is because while the directory

ansible

is found under

/etc

,

facts.d

is not. Therefore,

CDPATH

is useful for getting to the top of a tree that you access frequently, but it may get cumbersome to manage when you're browsing a large folder structure.</p> <p>Finally, let's talk about two common use cases that everyone does at some point: Changing a file extension and renaming files. At first glance, this may sound like the same thing, but Bash offers a few different tricks to accomplish these tasks.</p> <p>While it may be a „down-and-dirty“ operation, most users at some point need to create a quick copy of a file they are working on. Most will copy the filename exactly and simply append a file extension like

.old

or

.bak

. There is a quick shortcut for this in Bash. Suppose you have a filename like

spideroak_inotify_db.07pkh3

that you want to keep a copy of. You could type:</p> <pre class=„bash geshifilter-bash“>cp spideroak_inotify_db.07pkh3 spideroak_inotify_db.07pkh3.bak</pre> <p>You can make quick work of this by using copy/paste operations, using the tab complete, possibly using one of the shortcuts to repeat an argument, or simply typing the whole thing out. However, the command below should prove even quicker once you get used to typing it:</p> <pre class=„bash geshifilter-bash“>cp spideroak_inotify_db.07pkh3{,.old}</pre> <p>This (as you can guess) copies the file by appending the

.old

file extension to the file. That's great, you might say, but I want to rename a large number of files at once. Sure, you <em>could</em> write a for loop to deal with these (and in fact, I often do this for something complicated) but why would you when there is a handy utility called

rename

? There is some difference in the usage of this utility between Debian/Ubuntu and CentOS/Arch. The Debian-based rename uses a SED-like syntax:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„11“>user@ubuntu-1604:/tmp$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done <p>user@ubuntu-1604:/tmp$ ls old_text_file_*<br/>old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt<br/>old_text_file_2.txt old_text_file_4.txt</p> <p>user@ubuntu-1604:/tmp$ rename 's/old_text_file/shiney_new_doc/' *.txt</p> <p>user@ubuntu-1604:/tmp$ ls shiney_new_doc_*<br/>shiney_new_doc_1.txt shiney_new_doc_3.txt shiney_new_doc_5.txt<br/>shiney_new_doc_2.txt shiney_new_doc_4.txt</p> </div> <p>On a CentOS or Arch box it would look similar:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„11“>[user@centos /tmp]$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done <p>[user@centos /tmp]$ ls old_text_file_*<br/>old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt<br/>old_text_file_2.txt old_text_file_4.txt</p> <p>[user@centos tmp]$ rename old_text_file centos_new_doc *.txt</p> <p>[user@centos tmp]$ ls centos_new_doc_*<br/>centos_new_doc_1.txt centos_new_doc_3.txt centos_new_doc_5.txt<br/>centos_new_doc_2.txt centos_new_doc_4.txt</p> </div> <h2>Bash key bindings</h2> <p>Bash has a lot of built-in keyboard shortcuts. You can find a list of them by typing

bind -p

. I thought it would be useful to highlight several, although some may be well-known.</p> <p>&#160; &#160; ctrl + _ (undo)<br/>&#160; &#160; ctrl + t (swap two characters)<br/>&#160; &#160; ALT + t (swap two words)<br/>&#160; &#160; ALT + . (prints last argument from previous command)<br/>&#160; &#160; ctrl + x + * (expand glob/star)<br/>&#160; &#160; ctrl + arrow (move forward a word)<br/>&#160; &#160; ALT + f (move forward a word)<br/>&#160; &#160; ALT + b (move backward a word)<br/>&#160; &#160; ctrl + x + ctrl + e (opens the command string in an editor so that you can edit it before execution)<br/>&#160; &#160; ctrl + e (move cursor to end)<br/>&#160; &#160; ctrl + a (move cursor to start)<br/>&#160; &#160; ctrl + xx (move to the opposite end of the line)<br/>&#160; &#160; ctrl + u (cuts everything before the cursor)<br/>&#160; &#160; ctrl + k (cuts everything after the cursor)<br/>&#160; &#160; ctrl + y (pastes from the buffer)<br/>&#160; &#160; ctrl + l (clears screen)s</p> <p>I won't discuss the more obvious ones. However, some of the most useful shortcuts I have found are the ones that let you delete words (or sections of text) and undo them. Suppose you were going to stop a bunch of services using

systemd

, but you only wanted to start a few of them after some operation has completed. You might do something like this:</p> <p>systemctl stop httpd mariadb nfs smbd<br/>&lt;hit the up button to get the previous command&gt;<br/>&lt;use 'ctrl + w' to remove the unwanted arguments&gt;</p> <p>But what if you removed one too many? No problem&#8212;simply use

ctrl + _

to undo the last edit.</p> <p>The other cut commands allow you to quickly remove everything from the cursor to the end or beginning of the line (using

Ctrl + k

and

Ctrl + u

, respectively). This has the added benefit of placing the cut text into the terminal buffer so you can paste it later on (using

ctrl + y

). These commands are hard to demonstrate here, so I strongly encourage you to try them out on your own.</p> <p>Last but not least, I'd like to mention a seldom-used key combination that can be extremely handy in confined environments such as containers. If you ever have a command look garbled by previous output, there is a solution: Pressing

ctrl + x + ctrl + e

will open the command in whichever editor is set in the environment variable EDITOR. This will allow you to edit a long or garbled command in a text editor that (potentially) can wrap text. Saving your work and exiting, just as you would when working on a normal file, will execute the command upon leaving the editor.</p> <h2>Miscellaneous tips</h2> <p>You may find that having colors displayed in your Bash shell can enhance your experience. If you are using a session that does not have colorization enabled, below are a series of commands you can place in your

.bash_profile

to add color to your session. These are fairly straightforward and should not require an in-depth explanation:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„13“># enable colors<br/>eval „`dircolors -b`“ <p># force ls to always use color and type indicators<br/>alias ls='ls -hF –color=auto'</p> <p># make the dir command work kinda like in windows (long format)<br/>alias dir='ls –color=auto –format=long'</p> <p># make grep highlight results using color<br/>export GREP_OPTIONS='–color=auto'</p> <p># Add some colour to LESS/MAN pages<br/>export LESS_TERMCAP_mb=$'\E[01;31m'<br/>export LESS_TERMCAP_md=$'\E[01;33m'<br/>export LESS_TERMCAP_me=$'\E[0m'<br/>export LESS_TERMCAP_se=$'\E[0m'<br/>export LESS_TERMCAP_so=$'\E[01;42;30m'<br/>export LESS_TERMCAP_ue=$'\E[0m'<br/>export LESS_TERMCAP_us=$'\E[01;36m'</p> </div> <p>Along with adjusting the various options within Bash, you can also use some neat tricks to save time. For example, to run two commands back-to-back, regardless of each one's exit status, use the

;

to separate the commands, as seen below:</p> <pre class=„bash geshifilter-bash“>[user@centos /tmp]$ du -hsc * ; df -h</pre> <p>This simply calculates the amount of space each file in the current directory takes up (and sums it), then it queries the system for the disk usage per block device. These commands will run regardless of any errors generated by the

du

command.</p> <p>What if you want an action to be taken upon successful completion of the first command? You can use the

&amp;&amp;

shorthand to indicate that you want to run the second command only if the first command returns a successful exit status. For example, suppose you want to reboot a machine only if the updates are successful:</p> <pre class=„bash geshifilter-bash“>[root@arch ~]$ pacman -Syu –noconfirm &amp;&amp; reboot</pre> <p>Sometimes when running a command, you may want to capture its output. Most people know about the

tee

command, which will copy standard output to both the terminal and a file. However, if you want to capture more complex output from, say,

strace

, you will need to start working with <a href=„https://www.digitalocean.com/community/tutorials/an-introduction-to-linux-i-o-redirection“ target=„_blank“>I/O redirection</a>. The details of I/O redirection are beyond the scope of this short article, but for our purposes we are concerned with

STDOUT

and

STDERR

. The best way to capture exactly what you are seeing is to combine the two in one file. To do this, use the

2&gt;&amp;1

redirection.</p> <pre class=„bash geshifilter-bash“>[root@arch ~]$ strace -p 1140 &gt; strace_output.txt 2&gt;&amp;1</pre> <p>This will put all of the relevant output into a file called

strace_output.txt

for viewing later.</p> <p>Sometimes during a long-running command, you may need to pause the execution of a task. You can use the 'stop' shortcut

ctrl + z

to stop (but not kill) a job. The job gets added to the job queue, but you will no longer see the job until you resume it. This job may be resumed at a later time by using the foreground command

fg

.</p> <p>In addition, you may also simply pause a job with

ctrl + s

. The job and its output stay in the terminal foreground, and use of the shell is <em>not</em> returned to the user. The job may be resumed by pressing

ctrl + q

.</p> <p>If you are working in a graphical environment with many terminals open, you may find it handy to have keyboard shortcuts for copying and pasting output. To do so, use the following shortcuts:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„7“># Copies highlighted text<br/>ctrl + shift + c <p># Pastes text in buffer<br/>ctrl + shift + v</p> </div> <p>Suppose in the output of an executing command you see another command being executed, and you want to get more information. There are a few ways to do this. If this command is in your path somewhere, you can run the

which

command to find out where that command is located on your disk:</p> <p>[root@arch ~]$ which ls<br/>/usr/bin/ls</p> <p>With this information, you can inspect the binary with the

file

command:</p> <p>[root@arch ~]$ file /usr/bin/ls<br/>/usr/bin/ls: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d4e02b88e596e4f82c6cc62a5bc4ce5827209a49, stripped</p> <p>You can see all sorts of information, but the most important for most users is the

ELF 64-bit LSB

nonsense. This essentially means that it is a precompiled binary as opposed to a script or other type of executable. A related tool you can use to inspect commands is the

command

tool itself. Simply running

command -V &lt;command&gt;

will give you different types of information:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„9“>[root@arch ~]$ command -V ls<br/>ls is aliased to `ls –color=auto` <p>[root@arch ~]$ command -V bash<br/>bash is /usr/bin/bash</p> <p>[root@arch ~]$ command -V shopt<br/>shopt is a shell builtin</p> </div> <p>Last but definitely not least, one of my favorite tricks, especially when working with containers or in environments where I have little knowledge or control, is the

echo

command. This command can be used to do everything from checking to make sure your

for

loop will run the expected sequence to allowing you to check if remote ports are open. The syntax is very simple to check for an open port:

echo &gt; /dev/&lt;udp or tcp&gt;/&lt;server ip&gt;/&lt;port&gt;

. For example:</p> <div class=„geshifilter bash geshifilter-bash“ readability=„9“>user@ubuntu-1604:~$ echo &gt; /dev/tcp/192.168.99.99/222<br/>-bash: connect: Connection refused<br/>-bash: /dev/tcp/192.168.99.99/222: Connection refused <p>user@ubuntu-1604:~$ echo &gt; /dev/tcp/192.168.99.99/22</p> </div> <p>If the port is closed to the type of connection you are trying to make, you will get a

Connection refused

message. If the packet is successfully sent, there will be no output.</p> <p>I hope these tips make Bash more efficient and enjoyable to use. There are many more tricks hidden in Bash than I've listed here. What are some of your favorites?</p> <h3>Appendix 1. List of tips and tricks covered</h3> <div class=„geshifilter bash geshifilter-bash“ readability=„39“># History related<br/>ctrl + r (reverse search)<br/>!! (rerun last command)<br/>!* (reuse arguments from previous command)<br/>!$ (use last argument of last command)<br/>shopt -s histappend (allow multiple terminals to write to the history file)<br/>history | awk 'BEGIN {FS=„[ \t]+|\\|“} {print $3}' | sort | uniq -c | sort -nr | head (list the most used history commands) <p># File and navigation<br/>cp /home/foo/realllylongname.cpp{,-old}<br/>cd -<br/>rename 's/text_to_find/been_renamed/' *.txt<br/>export CDPATH='/var/log:~' (variable is used with the cd built-in.)</p> <p># Colourize bash</p> <p># enable colors<br/>eval „`dircolors -b`“<br/># force ls to always use color and type indicators<br/>alias ls='ls -hF –color=auto'<br/># make the dir command work kinda like in windows (long format)<br/>alias dir='ls –color=auto –format=long'<br/># make grep highlight results using color<br/>export GREP_OPTIONS='–color=auto'</p> <p>export LESS_TERMCAP_mb=$'\E[01;31m'<br/>export LESS_TERMCAP_md=$'\E[01;33m'<br/>export LESS_TERMCAP_me=$'\E[0m'<br/>export LESS_TERMCAP_se=$'\E[0m' # end the info box<br/>export LESS_TERMCAP_so=$'\E[01;42;30m' # begin the info box<br/>export LESS_TERMCAP_ue=$'\E[0m'<br/>export LESS_TERMCAP_us=$'\E[01;36m'</p> <p># Bash shortcuts<br/>&#160; &#160; shopt -s cdspell (corrects typoos)<br/>&#160; &#160; ctrl + _ (undo)<br/>&#160; &#160; ctrl + arrow (move forward a word)<br/>&#160; &#160; ctrl + a (move cursor to start)<br/>&#160; &#160; ctrl + e (move cursor to end)<br/>&#160; &#160; ctrl + k (cuts everything after the cursor)<br/>&#160; &#160; ctrl + l (clears screen)<br/>&#160; &#160; ctrl + q (resume command that is in the foreground)<br/>&#160; &#160; ctrl + s (pause a long running command in the foreground)<br/>&#160; &#160; ctrl + t (swap two characters)<br/>&#160; &#160; ctrl + u (cuts everything before the cursor)<br/>&#160; &#160; ctrl + x + ctrl + e (opens the command string in an editor so that you can edit it before it runs)<br/>&#160; &#160; ctrl + x + * (expand glob/star)<br/>&#160; &#160; ctrl + xx (move to the opposite end of the line)<br/>&#160; &#160; ctrl + y (pastes from the buffer)<br/>&#160; &#160; ctrl + shift + c/v (copy/paste into terminal)</p> <p># Running commands in sequence<br/>&amp;&amp; (run second command if the first is successful)<br/>; (run second command regardless of success of first one)</p> <p># Redirecting I/O<br/>2&gt;&amp;1 (redirect stdout and stderr to a file)</p> <p># check for open ports<br/>echo &gt; /dev/tcp/&lt;server ip&gt;/&lt;port&gt;<br/>`` (use back ticks to shell out)</p> <p># Examine executable<br/>which &lt;command&gt;<br/>file &lt;path/to/file&gt;<br/>command -V &lt;some command binary&gt; (tells you whether &lt;some binary&gt; is a built-in, binary or alias)</p> </div> </html>

Cookies helfen bei der Bereitstellung von Inhalten. Diese Website verwendet Cookies. Mit der Nutzung der Website erklären Sie sich damit einverstanden, dass Cookies auf Ihrem Computer gespeichert werden. Außerdem bestätigen Sie, dass Sie unsere Datenschutzerklärung gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information