Bash 命令行日常使用技巧

这些隐藏的功能和快捷方式将帮助您更有效地执行一些最常见的 Bash 功能。
317 位读者喜欢这篇文章。
command key on keyboard

AW2SUM on Pixabay, CC0

作为许多 Linux 和 Unix 变体的默认 shell,Bash 包含各种未充分利用的功能,因此很难决定要讨论什么。最终,我决定专注于使日常活动更轻松的 Bash 技巧。

作为一名顾问,我看到了各种不同的环境和工作方式。我借鉴了这些经验,将技巧缩小到四个大类:终端和行技巧、导航和文件、历史记录以及有用的命令。这些类别完全是任意的,更多的是为了组织我自己的思路,而不是作为任何类型的权威分类。这里包含的许多技巧可能在主观上适合多个类别。

事不宜迟,以下是我遇到的一些最有用的 Bash 技巧。

使用 Bash 历史记录

提高工作效率的最佳方法之一是学习更有效地使用 Bash 历史记录。考虑到这一点,您可以在多用户环境中进行的最重要的调整之一可能是启用 shell 的 histappend 选项。为此,只需运行以下命令

shopt -s histappend 

这允许多个终端会话同时写入历史记录。在大多数环境中,此选项启用。这意味着如果您打开多个 Bash 会话(本地或通过 SSH),历史记录通常会丢失。

另一个常见的任务是使用 sudo 重复上一个命令。例如,假设您要创建一个目录 mkdir /etc/ansible/facts.d。除非您是 root 用户,否则此命令将失败。据我观察,大多数用户会按 up 箭头键,滚动到行首,然后添加 sudo 命令。有一种更简单的方法。只需像这样运行命令

sudo !!

Bash 将运行 sudo,然后运行上一个命令的全部内容。以下是在顺序运行时它的确切外观

[user@centos ~]$ mkdir -p /etc/ansible/facts.d
mkdir: cannot create directory ‘/etc/ansible’: Permission denied

[user@centos ~]$ sudo !!
sudo mkdir -p /etc/ansible/facts.d

!! 运行时,完整命令将回显到终端,以便您知道刚刚执行了什么。

类似但使用频率低得多的是 !* 快捷方式。这告诉 Bash 您希望将上一个命令中的所有参数重复到当前命令中。这对于具有许多您想要重用的参数的命令可能很有用。一个简单的例子是创建一堆文件,然后更改它们的权限

[user@centos tmp]$ touch file1 file2 file3 file4
[user@centos tmp]$ chmod 777 !*
chmod 777 file1 file2 file3 file4

它仅在特定情况下方便,但它可以为您节省一些击键次数。

说到节省击键次数,让我们谈谈在您的历史记录中查找命令。大多数用户会这样做

history |grep <some command>

但是,有一种更简单的方法来搜索您的历史记录。如果您按下

ctrl + r

Bash 将对您的历史记录进行反向搜索。当您开始键入时,结果将开始出现。例如

(reverse-i-search)`hist': shopt -s histappend

在上面的示例中,我键入了 hist,它匹配了我们之前介绍的 shopt 命令。如果您继续按 ctrl + r,Bash 将继续向后搜索所有其他匹配项。

我们的最后一个技巧与其说是一个技巧,不如说是一个有用的命令,您可以使用它来计数和显示历史记录中最常用的命令。

[user@centos tmp]$ history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head 
81 ssh
50 sudo
46 ls
45 ping
39 cd
29 nvidia-xrun
20 nmap
19 export

在此示例中,您可以看到 ssh 是目前我的历史记录中使用最多的命令。

导航和文件命名

您可能已经知道,如果您键入命令、文件名或文件夹名称,您可以按一次 tab 键来完成单词。这在存在单个完全匹配项时有效。但是,您可能不知道,如果您按两次 tab 键,它将显示基于您键入的所有匹配项。例如

[user@centos tmp]$ cd /lib <tab><tab>
lib/ lib64/ 

这对于文件系统导航非常有用。另一个有用的技巧是在您的 shell 中启用 cdspell。您可以通过发出 shopt -s cdspell 命令来执行此操作。这将有助于纠正您的拼写错误

[user@centos etc]$ cd /tpm
/tmp
[user@centos tmp]$ cd /ect
/etc

它并不完美,但点滴积累,汇流成河!

成功更改目录后,如果您需要返回到上一个目录怎么办?如果您没有深入目录树,这没什么大不了的。但是,如果您位于相当深的路径中,例如 /var/lib/flatpak/exports/share/applications/,您可以键入

cd /va<tab>/lib/fla<tab>/ex<tab>/sh<tab>/app<tab>

幸运的是,Bash 会记住您之前的目录,您只需键入 cd - 即可返回那里。以下是它的外观

[user@centos applications]$ pwd
/var/lib/flatpak/exports/share/applications

[user@centos applications]$ cd /tmp
[user@centos tmp]$ pwd
/tmp

[user@centos tmp]$ cd -
/var/lib/flatpak/exports/share/applications

这很好,但是如果您有很多目录想要轻松地在其中导航怎么办?Bash 也为您考虑到了这一点。您可以设置一个变量,这将有助于您更有效地导航。这是一个例子

[user@centos applications]$ export CDPATH='~:/var/log:/etc'
[user@centos applications]$ cd hp
/etc/hp

[user@centos hp]$ cd Downloads
/home/user/Downloads

[user@centos Downloads]$ cd ansible
/etc/ansible

[user@centos Downloads]$ cd journal
/var/log/journal

在上面的示例中,我设置了我的主目录(用波浪号 ~ 表示)、/var/log/etc。当您引用这些目录时,这些目录顶层的任何内容都将自动填充。不在 CDPATH 中列出的目录的根目录下的目录将不会被找到。例如,如果您要查找的目录是 /etc/ansible/facts.d/,则通过键入 cd facts.d 将无法完成。这是因为虽然在 /etc 下找到了目录 ansible,但没有找到 facts.d。因此,CDPATH 对于访问您经常访问的树的顶部非常有用,但当您浏览大型文件夹结构时,它可能会变得难以管理。

最后,让我们谈谈每个人在某个时候都会做的两个常见用例:更改文件扩展名和重命名文件。乍一看,这可能听起来是一回事,但 Bash 提供了几个不同的技巧来完成这些任务。

虽然这可能是一个“不太精细”的操作,但大多数用户在某些时候都需要创建他们正在处理的文件的快速副本。大多数人将完全复制文件名,并简单地附加一个文件扩展名,如 .old.bak。Bash 中有一个快速快捷方式可以做到这一点。假设您有一个文件名,如 spideroak_inotify_db.07pkh3,您想要保留一个副本。您可以键入

cp spideroak_inotify_db.07pkh3 spideroak_inotify_db.07pkh3.bak

您可以通过使用复制/粘贴操作、使用 tab 补全、可能使用快捷方式之一来重复参数,或者只是键入整个内容来快速完成此操作。但是,一旦您习惯了键入以下命令,它应该会更快

cp spideroak_inotify_db.07pkh3{,.old}

这(如您所能猜到的)通过将 .old 文件扩展名附加到文件来复制文件。您可能会说,这很棒,但我想一次重命名大量文件。当然,您可以编写一个 for 循环来处理这些文件(实际上,对于一些复杂的事情,我经常这样做),但是当有一个名为 rename 的方便实用程序时,为什么要这样做呢?在基于 Debian/Ubuntu 和 CentOS/Arch 的系统之间,此实用程序的使用方式存在一些差异。基于 Debian 的 rename 使用类似 SED 的语法

user@ubuntu-1604:/tmp$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done

user@ubuntu-1604:/tmp$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

user@ubuntu-1604:/tmp$ rename 's/old_text_file/shiney_new_doc/' *.txt

user@ubuntu-1604:/tmp$ ls shiney_new_doc_*
shiney_new_doc_1.txt shiney_new_doc_3.txt shiney_new_doc_5.txt
shiney_new_doc_2.txt shiney_new_doc_4.txt

在 CentOS 或 Arch 系统上,它看起来类似

[user@centos /tmp]$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done

[user@centos /tmp]$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

[user@centos tmp]$ rename old_text_file centos_new_doc *.txt

[user@centos tmp]$ ls centos_new_doc_*
centos_new_doc_1.txt centos_new_doc_3.txt centos_new_doc_5.txt
centos_new_doc_2.txt centos_new_doc_4.txt

Bash 键盘绑定

Bash 有很多内置的键盘快捷键。您可以通过键入 bind -p 找到它们的列表。我认为突出显示几个会很有用,尽管有些可能众所周知。

  • ctrl + _ (撤消)
  • ctrl + t (交换两个字符)
  • ALT + t (交换两个单词)
  • ALT + . (打印上一个命令的最后一个参数)
  • ctrl + x + * (展开 glob/星号)
  • ctrl + 向右箭头 (向前移动一个单词)
  • ALT + f (向前移动一个单词)
  • ALT + b (向后移动一个单词)
  • ctrl + x 然后 ctrl + e (在编辑器中打开命令字符串,以便您可以在执行前对其进行编辑)
  • ctrl + e (将光标移动到末尾)
  • ctrl + a (将光标移动到开头)
  • ctrl + xx (移动到行的另一端)
  • ctrl + u (剪切光标之前的所有内容)
  • ctrl + k (剪切光标之后的所有内容)
  • ctrl + y (从缓冲区粘贴)
  • ctrl + l (小写 L,清除屏幕)

我不会讨论更明显的快捷键。但是,我发现一些最有用的快捷键是那些允许您删除单词(或文本部分)并撤消它们的快捷键。假设您要使用 systemd 停止一堆服务,但您只想在某些操作完成后启动其中的几个服务。您可能会这样做

systemctl stop httpd mariadb nfs smbd
<hit the up button to get the previous command>
<use 'ctrl + w' to remove the unwanted arguments>

但是,如果您删除的太多了怎么办?没问题——只需使用 ctrl + _ 撤消上次编辑。

其他剪切命令允许您快速删除从光标到行尾或行首的所有内容(分别使用 Ctrl + kCtrl + u)。这还有一个额外的好处,即将剪切的文本放入终端缓冲区,以便您稍后粘贴它(使用 ctrl + y)。这些命令很难在这里演示,所以我强烈建议您自己尝试一下。

最后但并非最不重要的一点,我想提及一个很少使用的组合键,它在容器等受限环境中可能非常方便。如果您曾经遇到命令被之前的输出弄乱的情况,有一种解决方案:按下 ctrl + x + ctrl + e 将在环境变量 EDITOR 中设置的编辑器中打开该命令。这将允许您在文本编辑器中编辑长命令或乱码命令,该编辑器(可能)可以换行文本。保存您的工作并退出,就像您在处理普通文件时一样,将在离开编辑器后执行该命令。

杂项技巧

您可能会发现,在 Bash shell 中显示颜色可以增强您的体验。如果您使用的会话未启用着色,以下是一系列您可以放入 .bash_profile 中的命令,以向您的会话添加颜色。这些命令非常简单,不需要深入解释

# enable colors
eval "`dircolors -b`"

# force ls to always use color and type indicators
alias ls='ls -hF --color=auto'

# make the dir command work kinda like in windows (long format)
alias dir='ls --color=auto --format=long'

# make grep highlight results using color
export GREP_OPTIONS='--color=auto'

# Add some colour to LESS/MAN pages
export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m' 
export LESS_TERMCAP_so=$'\E[01;42;30m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

除了调整 Bash 中的各种选项外,您还可以使用一些巧妙的技巧来节省时间。例如,要背靠背运行两个命令,无论每个命令的退出状态如何,请使用 ; 分隔命令,如下所示

[user@centos /tmp]$ du -hsc * ; df -h

这只是计算当前目录中每个文件占用的空间量(并对其求和),然后查询系统以获取每个块设备的磁盘使用情况。这些命令将运行,而不管 du 命令生成的任何错误。

如果您希望在第一个命令成功完成后执行操作怎么办?您可以使用 && 简写形式来指示您只想在第一个命令返回成功退出状态时运行第二个命令。例如,假设您只想在更新成功后重启机器

[root@arch ~]$ pacman -Syu --noconfirm && reboot

有时在运行命令时,您可能想要捕获其输出。大多数人都知道 tee 命令,该命令会将标准输出复制到终端和文件。但是,如果您想从例如 strace 捕获更复杂的输出,您将需要开始使用I/O 重定向。I/O 重定向的详细信息超出了本文的范围,但就我们的目的而言,我们关注的是 STDOUTSTDERR。捕获您所看到的确切内容的最佳方法是将两者合并到一个文件中。为此,请使用 2>&1 重定向。

[root@arch ~]$ strace -p 1140 > strace_output.txt 2>&1

这将把所有相关输出放入一个名为 strace_output.txt 的文件中,以供稍后查看。

有时在长时间运行的命令期间,您可能需要暂停任务的执行。您可以使用“停止”快捷键 ctrl + z 来停止(但不是杀死)作业。作业被添加到作业队列中,但在您恢复作业之前,您将不再看到该作业。可以使用前台命令 fg 在稍后恢复此作业。

此外,您还可以使用 ctrl + s 简单地暂停作业。作业及其输出保留在终端前台,并且不会将 shell 的使用权返回给用户。可以通过按 ctrl + q 恢复作业。

如果您在打开多个终端的图形环境中工作,您可能会发现使用键盘快捷键复制和粘贴输出很方便。为此,请使用以下快捷键

# Copies highlighted text
ctrl + shift + c 

# Pastes text in buffer
ctrl + shift + v

假设在执行命令的输出中,您看到了另一个正在执行的命令,并且您想要获取更多信息。有几种方法可以做到这一点。如果此命令位于您的路径中的某个位置,则可以运行 which 命令来查找该命令在磁盘上的位置

[root@arch ~]$ which ls
/usr/bin/ls

有了这些信息,您可以使用 file 命令检查二进制文件

[root@arch ~]$ file /usr/bin/ls
/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

您可以看到各种信息,但对于大多数用户而言,最重要的是 ELF 64-bit LSB 这样的无意义信息。这本质上意味着它是一个预编译的二进制文件,而不是脚本或其他类型的可执行文件。您可以用来检查命令的相关工具是 command 工具本身。只需运行 command -V <command> 即可为您提供不同类型的信息

[root@arch ~]$ command -V ls
ls is aliased to `ls --color=auto` 

[root@arch ~]$ command -V bash
bash is /usr/bin/bash

[root@arch ~]$ command -V shopt
shopt is a shell builtin

最后但绝对不是最不重要的一点,我最喜欢的技巧之一,尤其是在使用容器或在我知之甚少或无法控制的环境中工作时,是 echo 命令。此命令可用于执行各种操作,从检查以确保您的 for 循环将按预期顺序运行,到允许您检查远程端口是否已打开。检查端口是否打开的语法非常简单:echo > /dev/<udp 或 tcp>/<server ip>/<port>。例如

user@ubuntu-1604:~$ echo > /dev/tcp/192.168.99.99/222
-bash: connect: Connection refused
-bash: /dev/tcp/192.168.99.99/222: Connection refused

user@ubuntu-1604:~$ echo > /dev/tcp/192.168.99.99/22

如果端口对您尝试建立的连接类型关闭,您将收到 Connection refused 消息。如果数据包成功发送,则不会有任何输出。

我希望这些技巧使 Bash 更高效且更易于使用。Bash 中隐藏的技巧远不止我在此处列出的这些。您最喜欢哪些技巧?

附录 1. 涵盖的技巧和窍门列表

# History related 
ctrl + r (reverse search)
!! (rerun last command)
!* (reuse arguments from previous command)
!$ (use last argument of last command)
shopt -s histappend (allow multiple terminals to write to the history file)
history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head (list the most used history commands)

# File and navigation
cp /home/foo/realllylongname.cpp{,-old}
cd -
rename 's/text_to_find/been_renamed/' *.txt
export CDPATH='/var/log:~' (variable is used with the cd built-in.)

# Colourize bash

# enable colors
eval "`dircolors -b`"
# force ls to always use color and type indicators
alias ls='ls -hF --color=auto'
# make the dir command work kinda like in windows (long format)
alias dir='ls --color=auto --format=long'
# make grep highlight results using color
export GREP_OPTIONS='--color=auto'

export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m' # end the info box
export LESS_TERMCAP_so=$'\E[01;42;30m' # begin the info box
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

# Bash shortcuts
    shopt -s cdspell (corrects typoos)
    ctrl + _ (undo)
    ctrl + arrow (move forward a word)
    ctrl + a (move cursor to start)
    ctrl + e (move cursor to end)
    ctrl + k (cuts everything after the cursor)
    ctrl + l (clears screen)
    ctrl + q (resume command that is in the foreground)
    ctrl + s (pause a long running command in the foreground)
    ctrl + t (swap two characters)
    ctrl + u (cuts everything before the cursor)
    ctrl + x + ctrl + e (opens the command string in an editor so that you can edit it before it runs)
    ctrl + x + * (expand glob/star)
    ctrl + xx (move to the opposite end of the line)
    ctrl + y (pastes from the buffer)
    ctrl + shift + c/v (copy/paste into terminal)

# Running commands in sequence
&& (run second command if the first is successful)
; (run second command regardless of success of first one)

# Redirecting I/O
2>&1 (redirect stdout and stderr to a file)

# check for open ports
echo > /dev/tcp/<server ip>/<port>
`` (use back ticks to shell out)

# Examine executable
which <command>
file <path/to/file>
command -V <some command binary> (tells you whether <some binary> is a built-in, binary or alias)
标签
User profile image.
Steve 是一位敬业的 IT 专业人士和 Linux 倡导者。在加入红帽之前,他曾在金融、汽车和电影行业工作多年。Steve 目前在红帽解决方案和技术实践部门担任架构师。他拥有从 RHCA(在 DevOps 领域)到 Ansible,再到容器化应用程序等各种认证。

10 条评论

嗨。
有些知道,有些不知道... 非常有趣!! :)
谢谢。

这真是一篇精彩的文章。
非常感谢您的分享。

我已经使用 bash 25 年了,仍然学到了一两个技巧 - 谢谢。

在更改目录的上下文中,pushd、popd 和 dirs 也可能值得一提... 我一直都在使用它们。

Steve,这是一篇非常informative和有用的文章。非常感谢您撰写这篇文章!

很棒的文章,Steve,但是为什么您为一篇关于 Bash 的文章选择了一张 Apple command 键的图片呢?

实际上我没有选择插图。那是编辑提供的。此外,还有很多人(尽管我不包括自己在这个群体中)在 Apple 硬件上使用 BASH

回复 作者:#NeverMac (未验证)

关于
shopt -s histappend

有一个线程不鼓励这种配置,
第一次看到这个,
因为我是 screen 和 tmux 的忠实粉丝,这总是困扰我,
但也许这个标志弊大于利。

https://news.ycombinator.com/item?id=10164053

真是一篇很棒的文章!使用 bash 多年了,我不知道其中的一些技巧。

我认为使用 shopt -s histappend 然后使用 sudo !! 不是一个好主意 - 如果更多人在同一个帐户下工作,您永远不知道上一个命令到底是什么。

感谢您的 Bash 键盘绑定!总是很有用!

© . All rights reserved.