---
title: Linux (十) - Shell 的基本概念和操作
categories: [Linux,Shell,Bash]
tags: [Linux,Shell,Bash]
date: 2021-01-06
---
電腦是由硬體和軟體所構成的,而主要負責運算的部分是作業系統的核心 - Kernel。當使用者下了命令之後,Kernel 就會接收這個命令並且再交由 CPU 進行處理。那 Kernel 是如何接收這些命令的呢 ?
<!-- more -->
可以看到下面這張圖,使用者和 Kernel 會靠著 `Shell` 作為一個使用者的介面來進行溝通,也就是說使用者下達了命令後,Shell 會將這些命令轉成 Kernel 可以理解的程式碼,再傳送給 Kernel 好讓 Kernel 可以正確地控制硬體工作。
![Kernel](https://cdn.mindmajix.com/blog/images/linux-0203-1919.png =300x300)
## BASH (Bourne Again SHell)
BASH 是 Linux 預設的 Shell,雖然 Shell 有很多種,但是 BASH 會被作為 Linux 預設的 Shell 是因為他的功能非常強大,下面列出幾個主要的優點 :
1. 命令紀錄 (history)
相信只要有碰過 Linux 或是 Windows 的 cmd 都會知道,只要命令輸入並執行過,下次只要按 `向下` 和 `向上` 鍵就可以找回輸入過的指令。這些輸入過的指令會先被記錄在 cache 中,當 Shell 被關閉時就會被寫入到 `~/.bash_history` 中,不過這個檔案只會保持一定數量的命令紀錄。
```bash=
$ cat ~/.bash_history
sudo yum update
ls
cd ~
```
2. 命令補全
當輸入命令時,可以只輸入指令或是檔案的前幾個字,接著按下 `tab` 鍵就可以將指令或是檔案補齊。
3. 命令別名
有些常用的命令可能比較長,這時候就可以使用 `alias` 指令來設定,將命令設成一個別名,之後只要輸入別名就可以執行了。
下面我們來實際操作一下,將 `ls -a` 這個指令設成 `la`,接著只要輸入 `la` 就可以達成 `ls -a` 的結果。此外,如果要知道有哪些別名的命令可以直接輸入 `alias` 來查看。
```bash=
$ alias la='ls -a'
$ la
'$RECYCLE.BIN'/ Program/
./ PROJECT/
../
$ alias
alias la='ls -a'
alias ll='ls -l'
alias ls='ls -F --color=auto --show-control-chars'
```
4. 程式化腳本 (Shell Script)
可以將多個指令寫在一個檔案中,達成連續下指令的動作。這可以大大簡化輸入常用指令的時間,有點類似於程式語言的作法。
5. 萬用字元 (Wildcard)
可以透過萬用字元 `*` 結合指令來進行操作。
## 查詢指令說明文件
想要查詢指令的說明文件可以使用 `help` 或 `man` 指令。但是在介紹這些指令前,要先知道指令有分為`內部指令 (builtin shell command)` 和 `外部指令`。內部命令是指本身已經內建於 Shell 的指令,而外部指令是額外安裝的應用,通常會放在 `/bin`、`/usr/bin`、`/sbin`、`/usr/sbin` 等等。
### help
help 指令只適用內部指令,如果要用於外部指令需使用 `指令 --help`。
* 內部指令使用 help 的方法如下 :
```bash=
help <builtin shell command>
```
**範例**
cd 屬於內建於 shell 的指令,所以可以使用 `help` 指令來查看說明。
```bash=
$ help cd
cd: cd [-L|[-P [-e]]] [dir]
Change the shell working directory.
Change the current directory to DIR. The default DIR is the value of the
HOME shell variable.
The variable CDPATH defines the search path for the directory containing
DIR. Alternative directory names in CDPATH are separated by a colon (:).
A null directory name is the same as the current directory. If DIR begins
with a slash (/), then CDPATH is not used.
If the directory is not found, and the shell option `cdable_vars' is set,
the word is assumed to be a variable name. If that variable has a value,
its value is used for DIR.
Options:
-L force symbolic links to be followed
-P use the physical directory structure without following symbolic
links
-e if the -P option is supplied, and the current working directory
cannot be determined successfully, exit with a non-zero status
The default is to follow symbolic links, as if `-L' were specified.
Exit Status:
Returns 0 if the directory is changed, and if $PWD is set successfully when
-P is used; non-zero otherwise.
```
* 外部指令使用 help 的方法如下 :
```bash=
<command> --help
```
**範例**
docker 是額外安裝的應用,所以屬於外部指令,可以看到直接用 `help docker` 就會失敗,改用 `docker --help` 可以成功列出說明文件。如果沒有安裝 docker 可以用 `ls` 來測試,ls 雖然很像是內部指令,但其實是外部指令。
```bash=
$ help docker
-bash: help: no help topics match `docker'. Try `help help' or `man -k docker' or `info docker'.
$ docker --help
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Options:
--config string Location of client config files (default "/home/opc/.docker")
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/home/opc/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/home/opc/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/home/opc/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
.
.
.
```
### man
man 指令可以比 help 指令得到更多詳細的說明,而且 man 也沒有區分內部或是外部指令,都可以使用。不過經過實測 `docker` 指令會發現說明內容反而比較少,還建議使用 `docker --help` 來查看,所以其實 `man` 和 `help` 可以混合著用,當在其中一個找不到時可以再用另一個指令找看看。
* man 指令使用方式如下 :
```bash=
man <command>
```
**範例**
下面這個範例使用 man 指令來查看 cd 指令的說明文件,可以看到列出了一堆指令在前面,要往下找才看的到 cd 的。這是因為 cd 是內部的指令,只要用 man 找內部指令就會列出所有內部指令的說明。建議要看內部指令可以搭配 `grep` 擷取要找的指令就好,例如 : `man cd | grep cd`。
```bash=
$ man cd
BASH_BUILTINS(1) General Commands Manual BASH_BUILTINS(1)
NAME
bash, :, ., [, alias, bg, bind, break, builtin, caller, cd, command, compgen, complete, compopt, continue, declare, dirs, disown, echo, enable, eval, exec,
exit, export, false, fc, fg, getopts, hash, help, history, jobs, kill, let, local, logout, mapfile, popd, printf, pushd, pwd, read, readonly, return, set,
shift, shopt, source, suspend, test, times, trap, true, type, typeset, ulimit, umask, unalias, unset, wait - bash built-in commands, see bash(1)
BASH BUILTIN COMMANDS
Unless otherwise noted, each builtin command documented in this section as accepting options preceded by - accepts -- to signify the end of the options. The
:, true, false, and test builtins do not accept options and do not treat -- specially. The exit, logout, break, continue, let, and shift builtins accept and
process arguments beginning with - without requiring --. Other builtins that accept arguments but are not specified as accepting options interpret arguments
beginning with - as invalid options and require -- to prevent this interpretation.
: [arguments]
No effect; the command does nothing beyond expanding arguments and performing any specified redirections. A zero exit code is returned.
. filename [arguments]
.
.
.
cd [-L|[-P [-e]]] [dir]
Change the current directory to dir. The variable HOME is the default dir. The variable CDPATH defines the search path for the directory containing
dir. Alternative directory names in CDPATH are separated by a colon (:). A null directory name in CDPATH is the same as the current directory, i.e.,
``.''. If dir begins with a slash (/), then CDPATH is not used. The -P option says to use the physical directory structure instead of following sym‐
bolic links (see also the -P option to the set builtin command); the -L option forces symbolic links to be followed. If the -e option is supplied with
-P, and the current working directory cannot be successfully determined after a successful directory change, cd will return an unsuccessful status. An
argument of - is equivalent to $OLDPWD. If a non-empty directory name from CDPATH is used, or if - is the first argument, and the directory change is
successful, the absolute pathname of the new working directory is written to the standard output. The return value is true if the directory was suc‐
cessfully changed; false otherwise.
.
.
.
```
## 查詢指令是否為內建指令
前面有提到指令有分為內部指令和外部指令,那要怎麼去分辨指令到底是內部的還是外部的呢 ? 可以使用 `type` 指令來查看。
### type
type 指令的使用方式如下 :
```bash=
type <option> <command>
```
option :
* -t : 顯示出指令的意義
* alias : 指令是用別名所設定
* builtin : 內部指令
* file : 外部指令
* -a : 列出指令的路徑
**範例**
下面就分別來看一下 `cd`、`ls`、`docker` 指令分別是屬於外部還是內部指令。首先可以看到 `cd` 很明確直接顯示屬於內建的指令。而 `ls` 是用別名設定的指令,並不是內建的指令。最後 `docker` 很明確的直接列出指令的路徑,並且顯示 `file` 就是外部指令。
```bash=
$ type cd
cd is a shell builtin
$ type -t cd
builtin
$ type -a cd
cd is a shell builtin
cd is /usr/bin/cd
---
$ type ls
ls is aliased to 'ls --color=auto'
$ type -a ls
ls is aliased to 'ls --color=auto'
ls is /usr/bin/ls
$ type -t ls
alias
---
$ type docker
docker is /usr/bin/docker
$ type -t docker
file
$ type -a docker
docker is /usr/bin/docker
```
`type -a <command>` 也可以用來找指令的路徑,和 `which <command>` 有一樣的效果,都可以找到指令執行檔的位置。
## 執行多個指令
當我們想要一次同時下多個指令時,可以透過 `;`, `&&` 和 `||` 來實現。但三者在使用上是有一點區別的,下面我們就直接透過範例來看。
**範例**
* 分別 `;`
如果只是要逐一的執行各個指令而不管指令的執行成功與否就可以透過 `;` 來實現。
```shell=
$ cd tmp ; mkdir tmp ; cd tmp ; pwd
bash: cd: tmp: No such file or directory # 一開始沒有 tmp
/user1/tmp # 後續指令依然繼續執行
```
* AND `&&`
如果指令有前後相依性的就必須使用 `&&` 來施行,`&&` 只會在前面的指令執行成功後才執行後面的指令。
```shell=
$ cd tmp && mkdir tmp ; ls
bash: cd: tmp: No such file or directory
# 一開始沒有 tmp 所以切換目錄失敗後就不會執行建立資料夾的動作
```
* OR `||`
`||` 的用法與 `&&` 相反,如果前一個指令執行失敗就會執行第二個,反之如果第一個執行成功就不會執行的二個。
```shell=
$ cd tmp || mkdir tmp ; ls
bash: cd: tmp: No such file or directory
tmp # 雖然第一個指令失敗但還是執行了第二個
$ mkdir tmp1 || mkdir tmp2 ; ls
tmp tmp1 # 第一個指令執行成功了就不會執行第二個
```
## 指令換行
有時候指令會很長,雖然很長並不會有問題,但是對自己來說卻很不好讀。這時候就可以在要換行的時候輸入 `\` + Enter 來跳離 Enter 鍵,此時游標就會跳到下一行就可以繼續輸入了。
**範例**
下面來看一下實際使用的方式,通常 docker 指令如果有很多設定都會寫很長,這時候使用換行就可以一目了然輸入的指令了。這裡只是要顯示使用的效果,如果不知道 docker 是甚麼也沒有關係,了解換行怎麼用就好。
可以看到在輸入反斜線+換行後,下一行的前面會出現 `>` 的圖案,這時候就代表可以繼續進行輸入了。
```bash=
$ docker run \
> -d \
> --restart always \
> --name portainer \
> -p 9000:9000 \
> -v /var/run/docker.sock:/var/run/docker.sock \
> -v /volume1/docker/portainer:/data \
> portainer/portainer
```
👁🗨 前面有提到換行後會出現 `>` 符號,這是自動生成的,若是預先打好指令要貼到 terminal 執行時不要自己加上 `>`,這樣反而會不能執行。所以如果要預先打好指令則要像下面這個範例 :
```bash=
docker run \
-d \
--restart always \
--name portainer \
-p 9000:9000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /volume1/docker/portainer:/data \
portainer/portainer
```
## 更換 Shell
如果想要切換到其他的 Shell,可以使用 `chsh` 指令,也就是 `change shell`。
```bash=
chsh <option> <shell name>
```
option :
* -s、`--shell` : 代表後面要指定更換的 Shell 名稱。
* -l、`--list-shell` : 列出支援的 Shell。
shell name : 要更換的 Shell,如果 option 是用 `-l` 要列出支援的 Shell 則不用填 shell name。
**範例**
下面這個範例列出了目前支援的 Shell,並且我們將 Shell 改成 bash。這就會用在如果你想要安裝其他的 Shell,在安裝完成後就可以透過 `chsh` 指令來做切換。
```bash=
$ chsh -l
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/zsh
$ chsh -s bash
```
## Summary
本篇簡單介紹了 Shell 的基本概念,也提供了如何查詢指令說明文件的方法,當不知道或是忘記了指令該怎麼用時,就可以快速的找到指令的說明文件。
## 參考
[1] [認識與學習BASH](http://linux.vbird.org/linux_basic/0320bash.php)
[2] [linux:幫助命令help、man、info](https://www.itread01.com/content/1525495089.html)
[3] [No man page for the cd command](https://stackoverflow.com/questions/41147818/no-man-page-for-the-cd-command)
[4] [善用 man 指令查詢 Linux 線上手冊(Man Page)](https://blog.gtwang.org/linux/linux-man-page-command-examples/)
###### tags: `Linux` `Shell` `Bash`