How to Write Bash
The basic grammer of shell script.
ref: shell tutorial
How to Write Bash
bash, or shell as bash is the most common text-based shell, see ,
In thim s post, the bash script can be analogied to other text-based shells, such as zsh.
Variable
assign variables in bash:
foo=bar
- Note that
foo = bar
will not work since it is interpreted as calling thefoo
program with arguments=
andbar
. In general, in shell scripts the space character will perform argument splitting.- 因此不要加空格
- Note that
access the value of the variable:
$foo
- 等价于
${foo}
, 花括号可以精确地界定变量名称的范围。
- 等价于
可以用
read
命令从标准输入接受数据并赋值:read val
example:
1
2
3
4
5#! /usr/bin/env bash
echo -n "Enter your name:"
read name
echo "hello $name"
exit 0
String
Strings in bash can be defined with
'
and"
delimiters, but they are not equivalent.Strings delimited with
'
are literal strings and will not substitute variable values whereas"
delimited strings will.1
2
3
4
5foo=bar
echo "$foo"
# prints bar
echo '$foo'
# prints $foo
Array
array initialization −
1
array_name=(value1 ... valuen)
assign
1
array_name[index]=value
access Array Values
1
${array_name[index]}
Quoting Mechanism
metacharacters
Unix Shell provides various metacharacters which have special meaning while using them in any Shell Script and causes termination of a word unless quoted.
1 | * ? [ ] ' " \ $ ; & ( ) | ^ < > new-line space tab |
quoting
The following table lists the four forms of quoting −
Sr.No. | Quoting & Description |
---|---|
1 | Single quote All special characters between these quotes lose their special meaning. |
2 | Double quote Most special characters between these quotes lose their special meaning with these exceptions −$`$'"\ |
3 | Backslash Any character immediately following the backslash loses its special meaning. |
4 | Back quote (aka backtick) Everything you type between backticks is evaluated (executed) by the shell before the main command, and the output of that execution is used by that command, |
The Single Quotes: 其内容不转义, 相当于在每个字符前加 backslash
The Double Quotes: 其内容转义
The Backslash: 取消其后面的一个字符的转义
The Backquotes:将其内容视作 command 并执行, 与后文的CMD substitution类似
1
var=`command`
1
2
3
4
5
6DATE=`date`
echo $DATE
#等价于后文的CMD substitution
echo $(DATE)
常见语法
条件语句
- As with most programming languages, bash supports control flow techniques including
if
,case
,while
andfor
.
if
syntax:
1 | if [ expression ] |
1 | if [ expression 1 ] |
紧凑形式: ;
(同一行上多个命令的分隔符)
example:
1 | if [ -f ~/.bashrc ]; then |
1 | #!/bin/sh |
case
syntax:
1 | case word in |
example:
1 | #!/bin/sh |
1 | #!/bin/sh |
select in && case in
select in语句自带循环
1 | select variable in value_list |
- variable: 表示变量
- value_list: 取值列表
- in: Shell关键字
select in 通常和 case in 一起使用,在用户输入不同的编号时可以做出不同的反应
example:
1 | #!/bin/sh |
该命令的while版本:
1 | while [ "$item" != "Finish" ]; |
循环语句
break
: 从for/while/until循环退出
for
syntax:
1 | for var in word1 word2 ... wordN |
example:
1 | for FILE in $HOME/.bash* |
1 | for f in *.png |
while
syntax:
1 | while condition |
example:
1 | quit=n |
1 | a=0 |
命令组合
分号串联:
1 | command1 ; command2 ; ... |
条件组合:
1 | statement1 && statement2 && statement3 && ... |
1 | statement1 || statement2 || statement3 || |
语句块
1 | { |
或
1 | { |
test expression 或 [ expression ]
[
(akatest
) command the and[[ ... ]]
test construct are used to evaluate expressions[
是一条命令, 与test
等价,大多数shell都支持。在现代的大多数sh实现中,[
与test
是builtin命令[]
将其operand直接当作argument
[[
,是关键字,许多shell(如ash bsh)并不支持这种方式[[]]
将其operand进行参数引用,算术扩展和CMD substitution, 不需要手动转义等
test
test expression:
1 | test 1 -lt 2 |
[ expression ]
:
1 | echo "Starting program at $(date)" # Date will be substituted |
与文件有关的条件测试
文件测试运算符的形式及功能
option | parameter | function |
---|---|---|
-r | 文件名 | 如文件存在并且是用户可读的,则测试条件为真 |
-w | 文件名 | 如文件存在并且是用户可写的,则测试条件为真 |
-x | 文件名 | 如文件存在并且是用户可执行的,则测试条件为真 |
-f | 文件名 | 如文件存在并且是普通文件,则测试条件为真 |
-d | 文件名 | 如文件存在并且是目录文件,则测试条件为真 |
-p | 文件名 | 如文件存在并且是命名的FIFO文件,则测试条件为真 |
-b | 文件名 | 如文件存在并且是块特殊文件,则测试条件为真 |
-c | 文件名 | 如文件存在并且是字符特殊文件,则测试条件为真 |
-s | 文件名 | 如文件存在并且文件长度大于0,则测试条件为真 |
-t | 文件描述符 | 如文件被打开且文件描述符是与终端设备相关的,则测试条件为真,默认文件描述符是1 |
字符串测试
option | parameter | function |
---|---|---|
-z | s1 | 如果字符串s1的长度为0,则测试条件为真 |
-n | s1 | 如果字符串s1的长度大于0,则测试条件为真 |
s1 | 如果字符串s1不是空字符串,则测试条件为真 | |
=或== | s1=s2 | 如果s1等于s2,则测试条件为真,“=”前后应有空格 |
!= | s1!=s2 | 如果s1不等于s2,则测试条件为真 |
< | s1<s2 | 如果按字典顺序s1在s2之前,则测试条件为真 |
> | s1>s2 | 如果按自定顺序s1在s2之后,则测试条件为真 |
数值测试
parameter | function |
---|---|
n1 -eq n2 | 如果整数n1等于n2,则测试条件为真 |
n1 -ne n2 | 如果整数n1不等于n2,则测试条件为真 |
n1 -lt n2 | 如果如果n1小于n2,则测试条件为真 |
n1 -le n2 | 如果如果n1小于或等于n2,则测试条件为真 |
n1 -gt n2 | 如果n1大于n2,则测试条件为真 |
n1 -ge n2 | 如果n1大于或等于n2,则测试条件为真 |
逻辑操作
parameter | function |
---|---|
! expr | 逻辑表达式求反 |
expr1 –a expr2 | 两个逻辑表达式“And“ |
expr1 –o expr2 | 两个逻辑表达式“Or“ |
Function
syntax
1 | function_name(){ |
example:
1 | mcd () { |
return
exit
: 不仅会退出函数, 还会退出执行该函数的shellreturn code
:仅仅退出函数. 和command的return code同
Function Call from Prompt
令shell加载函数定义:
可以将函数定义在主目录下的
.profile
,这样每次登陆后,在命令提示符后面输入函数名字就可以立即调用:1
func para1 para2
将函数定义写在一个文件( say
test.sh
), 然后执行它
令shell删除函数定义:
1
unset -f function_name
- 该命令也可用来令shell删除变量定义
- 反之,
set
可以用来定义变量
output
Commands will often return output using
STDOUT
, errors throughSTDERR
return code
- Commands have Return Code to report errors in a more script-friendly manner.
- 0 usually means everything went OK; anything different from 0 means an error occurred.
return code
, 你可以指定返回任何值
return code as bool value
Return codes can be used to conditionally execute commands using
&&
(and operator) and||
(or operator) .Commands can also be separated within the same line using a semicolon
;
.The
true
program will always have a 0 return code and thefalse
command will always have a 1 return code.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17false || echo "Oops, fail"
# Oops, fail
true || echo "Will not be printed"
#
true && echo "Things went well"
# Things went well
false && echo "Will not be printed"
#
true ; echo "This will always run"
# This will always run
false ; echo "This will always run"
# This will always run
Intro
Scripts vs shell functions
Some differences between shell functions and scripts that you should keep in mind are:
- Functions have to be in the same language as the shell, while scripts can be written in any language. This is why including a shebang for scripts is important.
- Functions are loaded once when their definition is read. Scripts are loaded every time they are executed. This makes functions slightly faster to load, but whenever you change them you will have to reload their definition.
- Functions are executed in the current shell environment( 可以简单理解为, function的所在路径是当前路径 ) whereas scripts execute in their own process. Thus, functions can modify environment variables, e.g. change your current directory, whereas scripts can’t. Scripts will be passed by value environment variables that have been exported using
export
- As with any programming language, functions are a powerful construct to achieve modularity, code reuse, and clarity of shell code. Often shell scripts will include their own function definitions.
Shell Programming Style
https://google.github.io/styleguide/shellguide.html
Shebang
Note that scripts need not necessarily be written in bash to be called from the terminal. For instance, here’s a simple Python script that outputs its arguments in reversed order:
1 | #!/usr/local/bin/python |
shebang: the character sequence consisting of
#!
at the beginning of a script in a Unix-like operating systemshell会将shebang中
#!
之后的内容作为一个程序的路径,打开该程序, 将本script的路径当作参数传入( 即: 将整个script当作input传入shebang所指定的程序 )For example, if a script is named with the path path/to/script, and it starts with the following line,
#!/bin/sh
, then the program loader is instructed to run the program /bin/sh, passing path/to/script as the first argument. In Linux, this behavior is the result of both kernel and user-space code.[9]The shebang line is usually ignored by the interpreter, because the "#" character is a comment marker in many scripting languages; some language interpreters that do not use the hash mark to begin comments still may ignore the shebang line in recognition of its purpose.
she-bang with env
The she-bang expects a full path to the interpreter to use so the following syntax would be incorrect:
1 | #!python |
Setting a full path like this might work:
1 | #!/usr/local/bin/python |
but would be non portable as python might be installed in /bin
, /opt/python/bin
, or wherever other location.
Using env
1 | #!/usr/bin/env python |
is a method allowing a portable way to specify to the OS a full path equivalent to the one where python
is first located in the PATH
.
Execute Shell Script
执行脚本文件:
直接指定
sh
来执行该脚本,不需要shebang,也不需要脚本有执行权限(因为该脚本直接作为参数传给了sh).1
sh script_file_path
需要写shebang来指定解释器, 并且要指定脚本路径.
必须加上
./
使得该command name被识别为一个路径名. 否则shell会继续在alias, builtin和PATH
中搜索该command name. 11
2chmod +x script_file ##(chown, chgrp optionally)
./script_file使用
.
或source
在当前shell session中执行该脚本1
source script_file
or
1
. script_file
因此, 该方法可以用于刷新当前shell环境:
1
source ~/.bashrc
Shell script是能在命令行直接输入的, 但仅会作用一次
注意: 方法1,2都是新开一个子shell session,在其中执行脚本,而方法3, 4是在当前shell session中执行脚本
I/O redirection
一般情况下,每个 Linux 命令运行时都会打开三个文件:
标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息
Discard the output
1
$ command > /dev/null
The file /dev/null is a special file that automatically discards all its input.
Discard both output of a command and its error output,
1
$ command > /dev/null 2>&1
- a command normally writes its output to STDOUT
- use standard redirection to redirect STDERR to STDOUT
- 这里
2>&1
将标准错误(2)合并到标准输出(1), 而标准输出已经被重定向到了/dev/null
, 因此总体效果是,标准错误和输出都被重定向到了/dev/null
file descriptor:
- 0 : STDIN
- 1: STDOUT
- 2: STDERR
Sr.No. | Command & Description |
---|---|
1 | pgm > file Output of pgm is redirected to file 会覆盖目标文件中原有的数据 |
2 | pgm < file Program pgm reads its input from file |
3 | pgm >> file Output of pgm is appended to file |
4 | n > file Output from stream with descriptor n redirected to file |
5 | n >> file Output from stream with descriptor n appended to file |
6 | n >& m Merges output from stream n with stream m |
7 | n <& m Merges input from stream n with stream m |
8 | << tag Standard input comes from here through next tag at the start of line |
9 | | Takes output from one program, or process, and sends it to another |
Here Document
Here Document 目前没有统一的翻译,这里暂译为”嵌入文档“。Here Document 是 Shell 中的一种特殊的重定向方式,它的基本的形式如下:
1 | command << delimiter |
它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command
注意:
- 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
- 开始的delimiter前后的空格会被忽略掉。
下面的例子,通过 wc -l 命令计算 document 的行数:
1 | wc -l << EOF |
也可将 Here Document 用在脚本中,例如:
1 | #!/bin/bash |
https://lyk-love.cn/2023/04/24/Shell-Script-Searching-Path/↩︎