Unix shell
shell 顾名思义为“壳”,主要是相对于系统内核来说的,可以理解为内核的“壳”,shell 负责将用户的指令解释并传给内核执行,直接接触内核是不被允许并危险的,所以 shell 就是一种命令解释器,常见的 shell有很多,分为图形界面 shell 和命令行 shell,命令行中最常用的就是 bash shell。
系统中,通过修改.bashrc
文件来配置使用环境,输入source ~/.bashrc
立刻使配置文件生效,如要配置全局生效,可修改/etc/bashrc
文件。
通过 cat /etc/shells
查看当前启用的 shell;通过 echo $SHELL
查看用户的默认登录 shell。
Bash shell
在 Bash 中,通过 <Tab> 自动补全,使用 ctrl-r 搜索命令行历史记录(按下按键之后,输入关键字便可以搜索,重复按下 ctrl-r 会向后查找匹配项,按下 Enter 键会执行当前匹配的命令,而按下右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改)。
输入 history
查看 1000 条命令行历史记录,再用 !n
(n
是命令编号)就可以再次执行该命令。
对文本的操作,有两个小技巧值得记忆:ctrl-a 可以将光标移至行首;ctrl-e 可以将光标移至行尾。
使用cd
命令可以切换工作路径,~
表示用户家目录,要访问家目录中的文件,可以使用前缀 ~
,例如 vi ~/.bashrc
。在脚本里则用环境变量 $HOME
指代家目录。
在两个工作目录之前来回切换是件很恼人的事情,可以使用cd -
命令来快速在二者之前切换。
Vim 编辑器
当程序步骤复杂时,bash 有其局限性,所以要求用户熟悉至少一种基于文本的编辑器,编辑器可以将你的命令按顺序执行,称为脚本。通常 Vim 会是最好的选择(除了 vim 还有 Emacs 等编辑器)。
Vim 是从 vi 发展出来的一个文本编辑器。代码补全、错误跳转等功能特别丰富,在程序员中被广泛使用。vi/vim 分为三种模式,分别是命令模式(Command mode) ,输入/插入模式(Insert mode) 和底线命令/命令行模式(Last line mode) 。 这三种模式的作用分别是:
命令模式:
用户启动vi/vim
,就进入了命令模式。
此状态下敲击键盘动作会被Vim
识别为命令,而非输入字符。命令模式只有一些最基本的命令(复制粘贴等等),因此通常需要底线命令模式扩展其功能。
输入模式/插入模式:
在命令模式下按i
键,进入输入模式。
按下键盘ESC
键,退出输入模式,切换到命令模式。完成文件的编辑后,在命令行模式中输入wq
存盘退出,也可使用ZZ
来达到同样的效果。
底线命令模式:
在命令模式下按下:
(英文冒号),进入底线命令模式。
底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。按ESC
键退出底线命令模式。
数据块模式:
使用control+v
进入数据块选中模式,I
进入插入模式,ESC
后会统一在数据块区域插入相同内容。
在Vim中输入":help user-manual"进入用户手册。也有手册中文版 可供参考。下面介绍了一些不同模式下的常用命令:
命令模式 命令行模式
复制 u # 撤回
dd # 剪切单行
yy # 复制单行
p # 粘贴
nG # 移动至文档第n行
G # 移动至文档最后一行
gg # = 1G 即移动至文档第一行
dd # 剪切一整行,并使用p来粘贴
D # 删除该行光标之后的所有字符
ndd # 向下剪切n行
* # 查找光标所在短语
< Shift > ^ # 光标跳转至行首字符
< Shift > $ # 光标跳转至行未字符
复制 :wq! # 强制存盘退出
:q! # 强制退出(不存盘退出
:x # 存盘退出(没修改不存盘,直接退出
:wq # 强制写入后存盘退出(即使在没有修改的情况下
:set number/:set nu # 显示行号
/var # 向光标下查找字符var
? var # 向光标上查找字符var
n # 重复前一个搜索动作
N # 反向重复前一个搜索动作
:n1,n2s/word1/word2/g # 在n1与n2行之间将word1替换为word2
: %s/word1/word2/g # 全文替换
: %s/ $ /word2/g # 行尾替换
: %s/^/word2/g # 行首替换
:set fileformat=unix # 格式win与unix回车等格式不一致,替换后可执行
在Vim中完成一个简单的脚本:
使用vim
来建立一个名为test.sh
的文件时,可以这样做:
并且在插入模式中写出脚本后存盘退出。
复制 #!/bin/bash # 标记为bash
echo "my first script!"
并用./test.ah
运行这个脚本。
echo命令 用于在shell中打印shell变量的值,或者直接输出指定的字符串。 Linux 的echo
命令,在 shell 编程中极为常用, 在终端下打印变量value
的时候也是常常用到的。当echo
使用-e
参数时,允许使用转译字符,如/t
是插入一个制表符。
文件恢复
在编辑文件的过程中,Vim 将会在当前目录中自动生成一个以.swp
结尾的临时交换文件,用于备份缓冲区中的内容,以便在意外退出时可以恢复之前编辑的内容。
完成编辑并保存退出后,临时交换文件会被删除;但如果 Vim 意外退出,那么这个临时文件就会留在硬盘中。当 Vim 再次启动时,会检查当前目录中是否存在交换文件。如果存在,则意味着 Vim 正在编辑此文件(他人正在编辑),或者在上次编辑过程中意外退出,这时Vim就会给出警告信息,并要求我们在以下四个选项中做出选择:
Open Read-Only(以只读方式打开):如果我们想要查看文件内容或是有另一个编辑过程正在运行,那么可以选择此选项;
Edit anyway(编辑文件):请尽量不要选择此选项。因为如果同时有两个或是多个编辑过程同时编辑一个文件,那么只有最后一个保存的编辑过程有效;
Recover(恢复):如果在编辑过程中vim意外退出,那么可以选择此选项尝试从交换文件恢复文档;
Quit(退出):选择此选项,将取消对此文件的修改。
已知 Vim 意外退出后,使用-r
参数直接恢复编辑。如vi -r filename
Help poor children in Uganda !
vim 是一款开源免费软件,不会向用户索取任何费用。但会建议用户向荷兰 ICCF 捐款,用于帮助乌干达的艾滋病患者:vim 的启动页会显示 「 Help poor children in Uganda! 」 的字样,中文版本中则是 「 请帮助乌干达的可怜孩童!」。
这是因为 vim 的作者 Bram Moolenaar 去过乌干达开展志愿服务,在那里他了解了当地儿童的极端生存状况,知道他们需要帮助,于是 Bram Moolenaar 回国后与其他人一起创立了 ICCF 基金会。通过 vim 捐赠的善款将全部用于帮助改善乌干达的儿童的饮食、医疗、教育等状况。
在命令行模式中输入 help uganda
可以看到详细的捐款方式。
变量用法与规定
复制 #!/bin/bash
MY_NAME = "zhang zi hao"
echo "you name is $MY_NAME"
#可以将命令执行后的输入结果赋值给一个变量
#!/bin/bash
LIST = $( ls )
SEVER_NAME = $( hostname )
假设执行 ./test.sh a b c
这样一个命令,则可以使用下面的值来获取对应参数:
$0
对应 "./test.sh
" 这个值。如果执行的是 ./work/test.sh
, 则对应 ./work/test.sh
这个值,而不是只返回文件名本身的部分(脚本中则代指所有域)
$2
会获取到 b
,即 $2
对应传给脚本的第二个参数
$3
会获取到 c
,即 $3
对应传给脚本的第三个参数。$4、$5
等参数的含义依此类推
$#
会获取到 3
,对应参数个数,统计的参数不包括$0
$@
会获取到"a" "b" "c"
,也就是所有参数的列表,不包括$0
$*
也会获取到"a" "b" "c"
, 其值和$@
相同。但 "$*
" 和 "$@
" 有所不同。$*
把所有参数合并成一个字符串 ,而 "$@
" 会得到一个字符串参数数组
$?
可以获取到执行./test.sh a b c
命令后的返回值。在执行一个前台命令后,可以立即用 $?
获取到该命令的返回值。该命令可以是系统自身的命令,可以是shell 脚本,也可以是自定义的 bash 函数
输入与输出 I/O
复制 #!/bin/bash
read -p "please enter your name:" NAME #-p 指定读取时的提字符
echo "your name is $NAME"
逻辑与判断
数值与逻辑判断语法
复制 var1 -eq var2 # true if var1 is equal to var2
var1 -ne var2 # true if var1 not equal to var2
var1 -lt var2 # true if var1 is less than var2
var1 -le var2 # true if var1 is less than or equal to var2
var1 -gt var2 # true if var1 greater than var2
var1 -ge var2 # true if var1 greater than or equal to var2
string1 != string2 # true if string1 not equal to straing2
-d FILE # True if FILE is a directory
-e FILE # True if FILE exists
-f FILE # True if FILE exists and is a regular file
-r FILE # True if FILE is readable
-s FILE # True if FILE exists and is not empty
-w FILE # True if FILE has write permission
-x FILE # True if FILE is executable
if 语句
复制 if [condition-is- true ]
then
command 1
command 2
...
fi
#if-else
if [condition-is- true ]
then
command 1
elif [condition-is- true ]
then
command 2
fi
迭代与循环
复制 #for 循环
for VARIABLE in ITEM
do
command 1
command 2
...
done
E.g.
复制 #!/bin/bash
COLORS = "red green blue"
for COLOR in $COLORS
do
echo "the color is: $COLOR"
done
#while 循环
while [ COMMAND IS TRUE ]
do
command 1
done
错误用法 [0 -eq 1] ;正确用法 [ 0 -eq 1 ]
命令的传递
符号 作用 逻辑或:前一个命令执行错误后第二个命令才会执行,否则第一个命令不执行。
逻辑与:当前一个命令正确执行后才会执行第二个命令。
不是所有的命令都支持管道符 ,如ls
类命令 (但可以配合xargs
的命令使用)
E.g. 将cat的输出结果作为grep的参数,只打印出包含“alias”的行
复制 [zhangzh@node01 ~ ]$ cat .bashrc
# .bashrc
#alias
alias cl = 'clear'
[zhangzh@node01 ~ ]$ cat .bashrc | grep alias
#alias
alias cl = 'clear'
E.g. 若 lab1 不存在,则创建 lab1
复制 [zhangzh@node01 ~ ]$ ll
total 4
drwx------ 2 zhangzh ybj 4096 May 8 16:17 linux
[zhangzh@node01 ~ ]$ ls lab1 || touch lab1
ls: cannot access 'lab1' : No such file or directory
[zhangzh@node01 ~ ]$ ll
total 4
-rw-r--r-- 1 zhangzh ybj 0 May 8 16:29 lab1
drwx------ 2 zhangzh ybj 4096 May 8 16:17 linux
E.g. 若存在 lab1 ,则删除 lab1
复制 [zhangzh@node01 ~ ]$ ll
total 4
-rw-r--r-- 1 zhangzh ybj 0 May 8 16:29 lab1
drwx------ 2 zhangzh ybj 4096 May 8 16:17 linux
[zhangzh@node01 ~ ]$ ls lab1 && rm lab1
lab1
[zhangzh@node01 ~ ]$ ll
total 4
drwx------ 2 zhangzh ybj 4096 May 8 16:17 linux
E.g. 使用分号将编译命令和运行命令连接在一起,以便在编译成功后立即运行程序
复制 gcc -o myprogram myprogram.c ; ./myprogram
Linux 文本三剑客
重定向符号
输入重定向符号
输出重定向符号
符号 作用 将标准输出和错误输出共同写入文件(在原有内容后追加)
复制 MacBook-Pro:code zhangzh $ ls -l xx
ls: xx: No such file or directory
MacBook-Pro:code zhangzh $ cat 1.txt
cat: 1.txt: No such file or directory
MacBook-Pro:code zhangzh $ vi 1.txt
MacBook-Pro:code zhangzh $ cat 1.txt
MacBook-Pro:code zhangzh $ ls -l xx > 1.txt
ls: xx: No such file or directory
MacBook-Pro:code zhangzh $ cat 1.txt
MacBook-Pro:code zhangzh $ ls -l xx 2> 1.txt
MacBook-Pro:code zhangzh $ cat 1.txt
ls: xx: No such file or directory
MacBook-Pro:code zhangzh $
awk 命令
数据可以来自标准输入,一个或多个文件。支持正则表达
格式:
复制 awk [options] 'script' var filename( s )
实例:
复制 [zhangzh@node01 shell]$ cat student.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
脚本通常由''
或""
包含
复制 [zhangzh@node01 shell]$ awk '$0 ~ /LuoHu/' student.dat # ~ !~ 匹配与不匹配正则运算符
Mary 116018 LuoHu W 65
[zhangzh@node01 shell]$ awk '/LuoHu/' student.dat
Mary 116018 LuoHu W 65
[zhangzh@node01 shell]$ awk '$0 !~ /LuoHu/' student.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Steven 116030 YanTian M 78
语句块通常由{}
包含
复制 [zhangzh@node01 shell]$ awk '{if ($5>80) print $1}' student.dat
Tom
John
有无空格不影响脚本执行,但如果需要打印空格,可以使用" "
包裹起来
复制 [zhangzh@node01 shell]$ awk '{if($5>80)print$1}' student.dat
Tom
John
指定某一位的字符,真则打印
复制 [zhangzh@node01 shell]$ awk '/n/' student.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ awk '/^.....n/' student.dat
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ awk '$0 !~ /^.....n/' student.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
NR
表示记录数,执行过程中对应行号
复制 [zhangzh@node01 shell]$ awk 'NR%2==1' student.dat #打印奇数行
Tom 116001 FuTian M 90
Mary 116018 LuoHu W 65
语句块 BEGIN{ commands } pattern{ commands } END{ commands }
BEGIN语句块 在awk开始从输入流中读取行 之前 被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表>头等语句通常可以写在BEGIN语句块中
END语句块 在awk从输入流中读取完所有的行 之后 即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块>中完成,它也是一个可选语句块
pattern语句块 中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行 { print } >,即打印每一个读取到的行,awk读取的每 一行都会执行该语句块
复制 [zhangzh@node01 shell]$ awk '{nlines++} END{print nlines}' student.dat
4
E.g. 仅复制文件
复制 [daq@node02 anisotropie]$ ll | awk '!/^d/' | awk '{print $9}' | xargs -I {} cp {} ../anisotropieLowEnergy/
sed 命令
stream editor 流编辑器,支持正则表达,功能非同凡响。处理模式为逐行读入输入文件,并通过执行sed命令或者脚本的操作,然后处理后的结果输出到标准输出。
格式
复制 sed [options] 'commands' filename( s )
sed [options] -f scriptfile filename( s )
指定显示
复制 [zhangzh@node01 shell]$ cat test.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ sed -n '2,3p' test.dat # -n:仅显示处理后的结果
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
[zhangzh@node01 shell]$ sed -n '/11/I p' test.dat # I:按包含字符打印(忽略大小写)
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ sed -n '/^Mary/p' test.dat # ^:打印以Mary开头的行
Mary 116018 LuoHu W 65
[zhangzh@node01 shell]$ sed -n '/00/I p' test.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
替换删除
复制 [zhangzh@node01 shell]$ sed 's/11/22/g' test.dat # s:替换指定字符;g:全文逐行替换
Tom 226001 FuTian M 90
John 226005 NanShan M 85
Mary 226018 LuoHu W 65
Steven 226030 YanTian M 78
[zhangzh@node01 shell]$ sed '4c\Key' test.dat # c\:将选定行修改为新文本
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Key
[zhangzh@node01 shell]$ sed '1d' test.dat
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ sed '1,3d' test.dat # d:删除选中行
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ cat test.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ sed -i '1,3d' test.dat # -i:直接编辑文件(不可撤回)
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ cat test.dat
Steven 116030 YanTian M 78
指定插入
复制 [zhangzh@node01 shell]$ sed '4a\Key' test.dat # a\:在第4行后添加文本
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
Key
[zhangzh@node01 shell]$ sed '4i\Key' test.dat # i\:在第4行前添加文本
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Key
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ sed -e 4i\Key test.dat # -e:以脚本运行
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Key
Steven 116030 YanTian M 78
匹配标记
复制 [zhangzh@node01 shell]$ sed 's/\w\+/[&]/g' test.dat
[Steven] [116030] [YanTian] [M] [78]
[zhangzh@node01 shell]$ sed 's/^Steven/&s/g' test.dat
Stevens 116030 YanTian M 78
[zhangzh@node01 shell]$ sed 's/YanTian/&s/g' test.dat
Steven 116030 YanTians M 78
组合表达
复制 [zhangzh@node01 shell]$ echo hello world | sed 's/hello/hellow/g' | sed 's/world/word/'
hellow word
[zhangzh@node01 shell]$ echo hello world | sed 's/hello/hellow/g ; s/world/word/'
hellow word
正则表达
复制 [zhangzh@node01 shell]$ cat test.dat
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
[zhangzh@node01 shell]$ sed '/^$/d' test.dat #删除空白行
Tom 116001 FuTian M 90
John 116005 NanShan M 85
Mary 116018 LuoHu W 65
Steven 116030 YanTian M 78
引用
如果表达式内包含字符串变量,则需要用双引号
复制 [zhangzh@node01 shell]$ test = hello
[zhangzh@node01 shell]$ echo hello WORLD | sed "s/$test/HELLO/"
HELLO WORLD
grep 命令
可以抓取你想要的字符,grep 命令可以抓取字符所在行的文本。常用的参数有:
-i
忽略大小写进行匹配;
-v
反向查找,只打印不匹配的行;
-r
递归查找。可以同时打印文件名和所在行内容,这在使用通配符查找时很有用。
复制 [zhangzh@node01 ~ ]$ cat .bashrc
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ];
then
. /etc/bashrc
fi
# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
PATH = "$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH
#ROOT environment
source /home/software/cern_root/root_install/bin/thisroot.sh
#alias
alias cl = 'clear'
[zhangzh@node01 ~ ]$ cat .bashrc | grep alias
#alias
alias cl = 'clear'