理解shell

GUI(Graphical User Interface 图形用户界面);
CLI(command-line interface,命令行界面);

shell不单单是一种CLI。它是一个时刻都运行的复制交互式程序。输入命令并利用shell来运行脚本会出现一些即有趣又令人困惑的问题。搞清楚shell进程以及它与系统之间的关系能够帮助你解决这些难题。

1. shell的类型

ls -l /bin/bash
ls -l /bin/tcsh //源自最初的C shell
ls - l /bin/dash //ash shell的Debian版本
ls -l /bin.csh //C shell的软连接指向的是tcsh shell

这些shell程序都可以被设置为用户默认的shell。不过由于bash shell的广为流传,很少有人使用其他的shell作为默认的shell。

默认的交互shell会在用户登录某个虚拟控制台终端或者GUI运行中运行终端仿真器时启动。不过还有另外一个默认的shell 是/bin/sh,它作为默认的系统shell,用于那些需要在启动时使用的系统shell脚本。

ls -l  /bin/sh //能看出 当前默认的shell是否被软连接将默认系统设置为了其他类型的shell。

并不是必须要一直使用默认的交互shell .可以使用发行版中所有的可以的shell,只需要输入对应的文件名。eg:

/bin/csh //就进入了该shell 可以输入exit来退出。

2. shell的父子关系

用于登录某个虚拟控制器终端或在GUI中运行终端仿真器时所启动的默认的交互shell,是一个父级shell。
在CLI提示符后面输入/bin/bash命令或在其他等效的bash命令时,会创建一个新的shell程序。这个shell程序被称为子shell。子shell也拥有CLI提示符,同样会等待命令输入。

bash
bash
exit

我们在输入一个bash之后一个子shell就出现了。我们可以通过PPID 进程ID来判断父子关系。

父子shell

对应bash命令的参数 我们可以通过man bash查看。bash --help命令也可以提供额外的帮助。
我们可以通过exit来退出shell。

就算是不使用bash命令或者运行shell脚本 也可生成子shell。一种方法就算使用进程列表。

3. 进程列表

你可以在一行中指定要依次运行的一系列命令。你可以通过命令列表来实现,只需要在命令之间加入(;)即可。

pwd ; ls ; cd /etc

上面的例子,所有的命令按照顺序执行,不存在任何问题。不过哟这并不是进程列表。要成为进程列表,这些命令需要包含在括号里面。

(pwd ; ls ; cd /etc)

二者执行结果看起来没什么不同,但起到的效果确是非同寻常。括号的加入使命令列表变成了进程列表。生成了一个子shell来执行对应的命令。

说明进程列表是一种命令分组
还有一种命令分组是将命令放在花括号中,并在命令列表的尾部加上分号。语法为{command;}。使用这种方式进行分组,并不会创建出子shell。

BASH_SUBSHELL变量

要知道是否生成了子shell 可以借助一个环境变量$BASH_SUBSHELL。如果为0 表示没有子shell,为1表示有。

在shell脚本中,经常使用子shell来进行多进程处理,但是采用子shell的成本不菲,会明显拖慢处理速度。
子shell同样存在问题。它并非真正的多进程处理,因为终端控制着子shell的I/O。

4. 别出心裁的子shell用法

在交互式的shell CLI中,还有很多更富有成效的子shell用法。进程列表、协程和管道都利用了子shell。它们都可以有效地在交互式shell中使用。 在交互式shell中,一个高效的子shell用法就是使用后台模式。在讨论如何将后台模式与子shell搭配使用之前,你得先搞明白什么是后台模式

4.1 后台模式

在后台模式中运行命令可以在处理命令的同时让出CLI,以供他用。演示后台模式的一个经典命令就是sleep 。 sleep 命令接受一个参数,该参数是你希望进程等待(睡眠)的秒数。这个命令在脚本中常用于引入一段时间的暂停。命令sleep 10 会将会话暂停10秒钟,然后返回shell CLI提示符。

sleep 10

要想将命令置入后台模式,可以在命令末尾加上字符& 。把sleep命令置入后台模式可以让我们利用ps 命令来小窥一番。

sleep 10&
ps -f 
//也可以使用jobs命令来显示当前运行在后台模式中的所有用户的进程(作业)
jobs

5. 外部命令

有时候也被称为文件系统命令,是存在bash shell之外的的程序。它们并不是shell程序的一部分。外部命令通常位于/bin,/usr/bin,/sbin,/user/sbin中。
ps就是一个外部命令。你可以使用which和type命令找到它。

当外部命令执行时,会创建一个子进程。这种操作被称为衍生。当进程必须执行衍生操作时,它需要花费时间和精力来设置新子进程的环境,所以外包命令多少还是有代价的。

6. 内建命令

内建与外部命令的区别在于前者不需要使用子进程来执行。它们已经和shell编译称为一体。和外部命令的区别在于前者不需要使用子进程来执行。它们已经和shell编译成了一体,作为shell工具的组成部分存在。不需要借助外部程序文件来运行。

cd 和 exit 命令都内建于bash shell。可以利用type 命令来了解某个命令是否是内建的。

type exit // type is a shell builtin
type cd //cd is a shell builtin

因为既不需要通过衍生出子进程来执行,也不需要打开程序文件,内建命令的执行速度要更快,效率也更高。。 要注意,有些命令有多种实现。例如echo 和 pwd 既有内建命令也有外部命令。两种实现略有不同。要查看命令的不同实现,使用type 命令的-a 选项。


 type -a pwd
//pwd is a shell builtin
//pwd is /bin/pwd

 type -a echo
 //echo is a shell builtin
 //echo is /bin/echo

6.1 使用history命令
一个有用的内建命令是history 命令。bash shell会跟踪你用过的命令。你可以唤回这些命令并重新使用。

要查看最近用过的命令列表,可以输入不带选项的history 命令。

history //一般会保留1000条

你可以设置保存在bash历史记录中的命令数。要想实现这一点,你需要修改名为HISTSIZE 的环境变量。

你可以唤回并重用历史列表中最近的命令。这样能够节省时间和击键量。 输入!! ,然后按回车键就能够唤出刚刚用过的那条命令来使用。

当输入!! 时,bash首先会显示出从shell的历史记录中唤回的命令。然后执行该命令。

命令历史记录被保存在隐藏文件.bash_history中,它位于用户的主目录中。

cat .bash_history

这里要注意的是,bash命令的历史记录是先存放在内存中,当shell退出时才被写入到历史文件中。

还有一些强制往历史文件里面插入记录 history -a
强制重新读取历史文件histoty -n (mac终端执行无效,对个会话终端打开的时候,使用该命令,强制重新读取文件中的记录)
!10349 唤回历史记录中的命令,只需要感叹号加上历史列表中的编号即可。

6.2 命名别名

alias 命令是另一个shell的内建命令。命令别名允许你为常用的命令(及其参数)创建另一个名称,从而将输入量减少到最低。 你所使用的Linux发行版很有可能已经为你设置好了一些常用命令的别名。要查看当前可用的别名,使用alias

alias

-='cd -'
...=../..
....=../../..
.....=../../../..
......=../../../../..
1='cd -'
2='cd -2'
3='cd -3'
4='cd -4'
5='cd -5'
6='cd -6'
7='cd -7'
8='cd -8'
9='cd -9'
_=sudo
afind='ack -il'
d='dirs -v | head -10'
g=git
ga='git add'
gaa='git add --all'
gapa='git add --patch'
gb='git branch'
gba='git branch -a'
gbd='git branch -d'
gbda='git branch --no-color --merged | command grep -vE "^(\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d'
gbl='git blame -b -w'
gbnm='git branch --no-merged'
gbr='git branch --remote'
gbs='git bisect'
gbsb='git bisect bad'
gbsg='git bisect good'
gbsr='git bisect reset'
gbss='git bisect start'
gc='git commit -v'
'gc!'='git commit -v --amend'
gca='git commit -v -a'
'gca!'='git commit -v -a --amend'
gcam='git commit -a -m'
'gcan!'='git commit -v -a --no-edit --amend'
'gcans!'='git commit -v -a -s --no-edit --amend'
gcb='git checkout -b'
gcd='git checkout develop'
gcf='git config --list'
gcl='git clone --recursive'
gclean='git clean -fd'
gcm='git checkout master'
gcmsg='git commit -m'
'gcn!'='git commit -v --no-edit --amend'
gco='git checkout'
gcount='git shortlog -sn'
gcp='git cherry-pick'
gcpa='git cherry-pick --abort'
gcpc='git cherry-pick --continue'
gcs='git commit -S'
gd='git diff'
gdca='git diff --cached'
gdct='git describe --tags `git rev-list --tags --max-count=1`'
gdt='git diff-tree --no-commit-id --name-only -r'
gdw='git diff --word-diff'
gf='git fetch'
gfa='git fetch --all --prune'
gfo='git fetch origin'
gg='git gui citool'
gga='git gui citool --amend'
ggpull='git pull origin $(git_current_branch)'
ggpur=ggu
ggpush='git push origin $(git_current_branch)'
ggsup='git branch --set-upstream-to=origin/$(git_current_branch)'
ghh='git help'
gignore='git update-index --assume-unchanged'
gignored='git ls-files -v | grep "^[[:lower:]]"'
git-svn-dcommit-push='git svn dcommit && git push github master:svntrunk'
gk='\gitk --all --branches'
gke='\gitk --all $(git log -g --pretty=%h)'
gl='git pull'
glg='git log --stat'
glgg='git log --graph'
glgga='git log --graph --decorate --all'
glgm='git log --graph --max-count=10'
glgp='git log --stat -p'
glo='git log --oneline --decorate'
globurl='noglob urlglobber '
glog='git log --oneline --decorate --graph'
gloga='git log --oneline --decorate --graph --all'
glol='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --abbrev-commit'
glola='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --abbrev-commit --all'
glp=_git_log_prettily
glum='git pull upstream master'
gm='git merge'
gmom='git merge origin/master'
gmt='git mergetool --no-prompt'
gmtvim='git mergetool --no-prompt --tool=vimdiff'
gmum='git merge upstream/master'
gp='git push'
gpd='git push --dry-run'
gpoat='git push origin --all && git push origin --tags'
gpristine='git reset --hard && git clean -dfx'
gpsup='git push --set-upstream origin $(git_current_branch)'
gpu='git push upstream'
gpv='git push -v'
gr='git remote'
gra='git remote add'
grb='git rebase'
grba='git rebase --abort'
grbc='git rebase --continue'
grbi='git rebase -i'
grbm='git rebase master'
grbs='git rebase --skip'
grep='grep  --color=auto --exclude-dir={.bzr,CVS,.git,.hg,.svn}'
grh='git reset HEAD'
grhh='git reset HEAD --hard'
grmv='git remote rename'
grrm='git remote remove'
grset='git remote set-url'
grt='cd $(git rev-parse --show-toplevel || echo ".")'
gru='git reset --'
grup='git remote update'
grv='git remote -v'
gsb='git status -sb'
gsd='git svn dcommit'
gsi='git submodule init'
gsps='git show --pretty=short --show-signature'
gsr='git svn rebase'
gss='git status -s'
gst='git status'
gsta='git stash save'
gstaa='git stash apply'
gstc='git stash clear'
gstd='git stash drop'
gstl='git stash list'
gstp='git stash pop'
gsts='git stash show --text'
gsu='git submodule update'
gts='git tag -s'
gtv='git tag | sort -V'
gunignore='git update-index --no-assume-unchanged'
gunwip='git log -n 1 | grep -q -c "\-\-wip\-\-" && git reset HEAD~1'
gup='git pull --rebase'
gupv='git pull --rebase -v'
gwch='git whatchanged -p --abbrev-commit --pretty=medium'
gwip='git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit -m "--wip--"'
history='fc -l 1'
l='ls -lah'
la='ls -lAh'
ll='ls -lh'
ls='ls -G'
lsa='ls -lah'
md='mkdir -p'
please=sudo
po=popd
pu=pushd
rd=rmdir
run-help=man
which-command=whence

重命名

alias gs='git status'

在定义好别名之后,你随时都可以在shell中使用它,就算在shell脚本中也没问题。要注意,因为命令别名属于内部命令,一个别名仅在它所被定义的shell进程中才有效。这样相当于我只能在我当前打开的进程中使用,而不能在其他进程使用,很有局限性。

删除重命名

unalias gs

若要每次登入都能够使用这些命令别名,则可将相应的alias命令存放到bash的初始化文件/etc/bashrc中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容