Bash外壳

用户对Bash shell的介绍

Bash是(截至今天) de facto shell on most systems you’ll get in touch with: Linux, macOS, and the WSL on Windows 10.

更新:自Catalina(2019年秋季)开始使用zsh的macOS

有一些历史原因使Bash成为世界上最受欢迎的外壳。早在1989年,当它第一次发布时,技术世界就大不一样了。当时,UNIX世界中的大多数软件都是封闭源代码。 Unix本身是专有的并且是封闭源代码。

要使用UNIX系统,必须使用外壳程序。

当时最流行的外壳是封闭源代码和专有的,您必须付费才能使用它。它是Bourne壳,可以在/bin/sh命令。之所以称为“伯恩(Bourne)”,是因为其创建者是史蒂夫·伯恩(Steve Bourne)。

理查德·斯托曼(Richard Stallman)在那些年与GNU项目(后来在Linux上一起使用)即将彻底改变一切,开始了开源革命。 GNU项目需要一个外壳,在自由软件基金会的帮助下,Bash诞生了。受Bourne Shell的大力启发,Bash的意思是伯恩再次壳它是GNU工程的重要组成部分,并且可能是我们今天仍在使用的最成功的软件之一。

Bash可以运行为该脚本编写的所有脚本sh,这是其采用的一项强制性功能,并且自初期起就引入了更多功能,从而为用户提供了更好的体验。从早期开始,Bash取得了许多进步。本教程描述了您可以使用的最受欢迎和最有用的功能。

Bash的第一步

由于Bash是许多系统中的默认shell,因此启动bash shell所需要做的只是

  • 登录到系统(如果是服务器)
  • 打开您的终端(如果是您的计算机)

看我的macOS终端指南有关在Mac上使用终端的更多信息。

启动后,您应该会看到一个提示(通常以$)。

您怎么知道shell正在运行bash?尝试输入help然后按Enter。

看?我们只是告诉Bash执行help命令。该命令依次向您显示您正在运行的Bash的版本以及可以使用的命令列表。

警告:看到我那里的版本了吗?是3.2.57。这是macOS随附的默认Bash版本,其中不包括用于许可问题的更高版本。该Bash版本是从2014年开始的。使用Homebrew来安装最新的Bash 5.x,方法是输入brew install bash

除非您正在创建,否则您绝不会使用bash帮助中列出的任何命令。外壳脚本或高级的东西。

每天有99%的外壳使用情况是在文件夹中导航并执行诸如lscd和其他公共UNIX实用程序。

要浏览文件系统,您将使用ls命令。它可以在/bin/ls,并且由于Bash具有/bin文件夹在其路径列表中,您只需输入ls使用它。

ls列出当前文件夹中的文件。您通常从主文件夹开始,该文件夹取决于系统,但在macOS下/Users。我的主文件夹在/Users/flavio。这与Bash无关,更多的是UNIX文件系统,但是参数重叠,并且如果您从未使用过Shell,那么很高兴知道这一点。

要导航到其他文件夹,请使用cd命令,然后是您要移动到的文件夹的名称:

cd Documents

cd ..返回到父文件夹。

根据您的Bash配置,您会看到当前文件夹显示在提示之前($象征)。或者您可能不知道,但是您始终可以通过键入以下内容来知道自己的位置pwd然后按Enter。

pwd表示* w * orking * d * irectory的* p * athname

命令行编辑

在外壳中编写命令时,请注意,您可以使用箭头键左右移动。这是一个外壳功能。您可以四处移动命令,按退格按钮并更正命令。按下enter键告诉外壳程序去让系统执行命令。

这是正常且可以接受的行为,但是这可能使早期的UNIX用户“赞叹不已”。

键盘组合使您可以快速进行编辑,而无需触及箭头键:

  • ctrl+d删除当前选择的字符
  • ctrl+f去右边的角色
  • ctrl+b去左边的角色

自动补全

在文件系统中移动时,Bash的一个不错的功能是自动补全。尝试输入cd Doc然后按tab使Bash自动完成的关键cd Documents。如果第一个字符有多个选择,Bash将返回您的列表,因此您可以输入更多几个字符以帮助消除歧义,然后按tab再次完成。

Shell可以自动完成文件名,也可以自动完成命令名。

Shell命令

使用外壳,我们可以运行系统上可用的命令。我们可以在命令前面加上完整路径(例如/bin/ls列出文件夹中的文件),但shell具有以下概念:小路所以我们可以输入ls并且它知道在哪里可以找到大多数命令(并且我们可以通过配置将文件夹添加到此路径)。

命令接受参数。例如ls /bin将列出该文件中的所有文件/bin文件夹。

参数以短划线开头-, 喜欢ls -a告诉ls还显示隐藏文件。按照惯例,隐藏文件是指以点(.)。

常见的Shell命令

在任何系统上都预装了很多命令,它们的差异很大,具体取决于运行的是Linux / macOS还是Linux发行版。

但是,让我们对您可以运行的最常见的shell命令进行简要汇总。 Shell本身并不提供这些命令,而是可以通过Shell调用的命令行命令。

每当遇到问题时,例如您不知道命令的功能或不知道如何使用它,请使用man。它使您可以获得我将列出的所有命令的帮助,以及更多。跑man ls例如。

这些是文件系统命令:

  • ls列出文件
  • cd更改文件夹
  • rm删除文件或文件夹
  • mv将文件移动到另一个文件夹,或更改文件名
  • cp复制文件
  • pwd显示当前工作目录
  • mkdir创建一个文件夹

Unix文件系统上的每个文件都有权限。chmod允许您更改那些内容(现在不进行讨论),并且chown允许您更改文件所有者

cattailgrep有两个用于处理文件的超级有用的命令。

piconanovimemacs是通常安装的编辑器。

whereis在macOS上,显示命令在系统上的位置。

当然,还有更多命令,但是您可能会经常遇到这些命令。

执行命令

lscd正如我提到的,这些命令位于/bin文件夹。只要是可执行文件,就可以通过键入其完整路径来执行任何文件,例如/bin/pwd。命令不需要在/bin文件夹,然后您可以使用./路径指示器。

例如,如果您有一个runme档案在/Users/flavio/scripts文件夹,您可以运行

cd /Users/flavio/scripts

然后运行./runme运行它。

或者你可以跑/Users/flavio/scripts/runme从Bash,无论您当前的文件夹在哪里。

职位

每当您运行命令时(如果它是一个运行时间较长的程序),您的外壳将完全归该命令所有。您可以使用以下命令终止该命令ctrl-C

您可以随时运行jobs查看您正在运行的作业及其状态。

$ jobs
Job	Group	State	Command
1	72292	stopped	ftp

Another useful command is ps which lists the processes running.

$ ps
  PID TTY           TIME CMD
19808 ttys000    0:00.56 /usr/local/bin/fish -l
65183 ttys001    0:04.34 -fish
72292 ttys001    0:00.01 ftp

top shows the processes and the resources they are consuming on your system.

A job or process can be killed using kill <PID>.

Command history

Pressing the up arrow key will show you the history of the commands you entered. Again, this is a shell feature. Pressing the down arrow key will let you navigate back and forth in time to see what commands you entered previously, and pressing enter will let you run that command again.

This is a quick access to the command history. Running the history command will show you all the commands entered in the shell:

When you start typing a command, Bash can autocomplete it by referencing a previously entered command in the history. Try it by pressing esc followed by tab.

To be honest I find the Fish shell implementation of this to be much better and easier

Setting your default shell

There are many more shells other than Bash. You have Fish, ZSH, TCSH, and others. Any user on the system can choose its own shell.

You set your default login shell by running the chsh -s chsh -s /bin/bash command. You’ll almost never need to do that, unless it was previously changed, for example with Fish:

chsh -s /usr/local/bin/fish

Customizing Bash

I noted before that you might (or might not) see your current working directory in the Bash prompt. Where is this determined? In the Bash configuration!

There’s a bit of confusion here because Bash uses a different configuration file for different scenarios, and it also reads multiple configuration files.

Let’s give some order to this confusion. First, there’s a big distinction whether Bash is initialized as a login shell or not. By login shell we mean that the system is not running a GUI (Graphical User Interface) and you log in to the system through the shell. That’s the case of servers, for example.

In this case, Bash loads this configuration file:

/etc/profile

and then looks in the user home folder and looks for one of these files, in order, executing the first one it finds:

~/.bash_profile
~/.bash_login
~/.profile

were ~ means your home folder (it’s automatically translated by Bash)

This means that if there’s a .bash_profile, ~/.bash_login and ~/.profile are never run, unless explicitly executed in .bash_profile.

If instead of being a login shell Bash is run like I do with macOS, for example, as a normal application, the configuration files change. Bash loads first /etc/bash.bashrc, then ~/.bashrc.

Environment variables

Sometimes you have programs that use environment variables. Those are values that you can set outside of the program, and alter the execution of the program itself. An API key, for example. Or the name of a file.

You can set an environment variable using the syntax

VARIABLE_NAME=variable_value

The value can contain white spaces, by using quotes

VARIABLE_NAME="variable value"

A bash script can use this value by prepending a dollar sign: $VARIABLE_NAME.

Also other programming languages commands can use environment variables, for example here’s how to read environment variables with Node.js.

The system sets up some environment variables for you, like

  • $HOME your home folder
  • $LOGNAME your user name
  • $SHELL the path to your default shell
  • $PATH the path where the shell looks for commands

You can inspect their value by prepending echo to them:

echo $LOGNAME # flavio
echo $HOME # /Users/flavio

A special environment variable: $PATH

I mentioned the $PATH variable. This is a list of folders where the shell will look into when you type a command. Folders are separated by a colon : and they are written in order - Bash will look into the first, search for the command you asked for, and run it if it finds it. Otherwise if goes to the next folder and so on.

My path is currently:

bash-5.0$ echo $PATH
/usr/local/bin:/usr/bin: /bin:/usr/sbin: /sbin:/usr/local/go/bin

You typically edit this in the ~/.bashrc file by prepending or appending items:

PATH = "$PATH:/Users/flavio/bin"

Aliases

Using aliases we can set up shortcuts for common commands. You can give a quick name to a complex combination of parameters, for example.

You define an alias using the syntax

alias <alias>=<command>

if there’s a space in the command you use quotes:

alias <alias>="<command>"

One alias I commonly add to my system is ll:

alias ll="ls --al"

You normally define aliases in your ~/.bashrc file.

Just be careful with quotes if you have variables in the command: using double quotes the variable is resolved at definition time, using single quotes it’s resolved at invokation time. Those 2 are different:

alias lsthis="ls $PWD"
alias lscurrent='ls $PWD'

$PWD refers to the current folder the shell is into. If you now navigate away to a new folder, lscurrent lists the files in the new folder, lsthis still lists the files in the folder you were when you defined the alias.

Advanced command line features

Wildcards

ls and many other commands can make great use of wildcards. You can list all files starting with image:

ls image*

Or all files ending with image:

ls *image

or all files that contain image inside the name:

ls *image*

Redirecting output and standard error error

By default, commands started in the shell print out both the output and errors back to the shell. This might not be what you want. You can decide to write the output to a file instead.

Actually, to a /different file/, because in Unix even the screen is considered to be a file. In particular,

  • 0 identifies the standard input
  • 1 identifies the standard output
  • 2 identifies the standard error

You can redirect the standard output to a file by appending 1> after a command, followed by a file name.

Using the same technique you can use 2> to redirect the standard error.

There is a shortcut > for 1>, since that is used quite a lot.

Example:

ls 1> list.txt 2> error.txt
ls > list.txt 2> error.txt

Another shortcut, &>, redirects /both/ standard output and standard error to a file.

ls &> output.txt

Another frequent thing is to redirect standard error to standard output using 2>&1.

Running a command in the background

You can tell Bash to run a program in the background without it taking control of the shell, by appending & after it:

top &

top is a command that lists the processes running, ordered by most resource consuming.

The application that would normally get control of the shell, is now started but nothing seems to happen. You can bring it back into focus by typing fg (aka *f*ore*g*round), but now we’re entering in the realm of processes and jobs which is a big topic on its own, but a quick overview:

When a command is running you can use ctrl-Z to pause it and bring it to the background. The shell comes back again in the foreground, and you can now run bg to move resume execution of that previously paused job.

When you are ready to go back to it, run fg to bring back that program in the foreground.

You can see all processes running using ps, and the list shows all the processes pid numbers. Using the paused process pid, you can bring to foreground a specific command, for example fg 72292. Same works for bg.

Queuing commands

You can instruct Bash to run a command right after another ends by separating them with a semicolon:

cd /bin; ls

You can repeat this to queue multiple commands in the same line.

Output redirection

A program can receive input from any file using the < operator, and save to a file the output using the > operator:

echo hello > result.txt
wc < result.txt

wc is a command that counts the words it receives as input.

Pipe

Using pipes, any command output can be used as input for a second command. Use the | operator to combine the two. In this example, wc gets its input from the output of echo hello:

echo hello | wc

Grouping commands

Use && to combine two commands using “and”. If the first command executes without problems, run the second, and so on.

Use || to combine two commands using “or”. If the first command executes without problems the second does not run.

! negates the next logical operation:

$ echo hello && echo test
hello
test
$ echo hello || echo test
hello
$ ! echo hello || echo test
hello
test

You can use parentheses to combine expressions to avoid confusion, and also to change the precedence:

$ ! (echo hello) || (echo test)
hello
test
$ ! (echo hello || echo test)
hello

Programming shells

One of the best features of a shell like Bash is its ability to create programs with it, by basically automating commands execution.

I’m going to write a separate guide on Bash scripting soon, which will be separate from this tutorial because the topic is really more in-depth than what I want to add in this introductory Bash guide.

A quick intro though: a script is a text file that starts with a line that indicates that it’s a shell script (and what shell it requires), followed by a list of commands, one per line. Example:

#!/bin/bash
ls

You can save this as a file myscript and make it executable using chmod +x myscript, and run it using ./myscript (./ means “current folder”).

Shell scripting is outside of the scope of this post, but I want you to know about this. Scripts can have control structures and many other cool things.

This same scripting strategy works for other shells, like Zsh:

#!/bin/zsh
ls

Download my free Linux Commands Handbook


More cli tutorials: