Open bingoohuang opened 5 years ago
Double parenthesis with and without dollar
$(...) means execute the command in the parens in a subshell and return its stdout. Example:
$ echo "The current date is $(date)"
The current date is Mon Jul 6 14:27:59 PDT 2015
Why is $(...) preferred over `...` (backticks)? `...` is the legacy syntax required by only the very oldest of non-POSIX-compatible bourne-shells. More
(...) means run the commands listed in the parens in a subshell. Example:
$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1
$((...)) means perform arithmetic and return the result of the calculation. Example:
$ a=$((2+3)); echo "a=$a"
a=5
((...)) means perform arithmetic, possibly changing the values of shell variables, but don't return its result. Example:
$ ((a=2+3)); echo "a=$a"
a=5
((a=$a+7)) # Add 7 to a
((a = a + 7)) # Add 7 to a. Identical to the previous command.
((a += 7)) # Add 7 to a. Identical to the previous command.
((a = RANDOM % 10 + 1)) # Choose a random number from 1 to 10.
# % is modulus, as in C.
if ((a > 5)); then echo "a is more than 5"; fi
# In case it wasn't obvious, the (( )) in a C-style for command are a math context. Or three separate math contexts, depending on your point of view.
for ((i=0, j=0; i<100; i++, j+=5)); do ...
${...} means return the value of the shell variable named in the braces. Example:
$ echo ${SHELL}
/bin/bash
{...} means execute the commands in the braces as a group. Example:
$ false || { echo "We failed"; exit 1; }
We failed
<<<
denotes a here string.
$ cat <<< 'hi there'
hi there
It passes the word on the right to the standard input of the command on the left.
<<
denotes a here document.
$ cat <<EOF
> hi
> there
> EOF
hi
there
EOF can be any word.
Here documents are commonly used in shell scripts to create whole files or to display long messages.
cat > some-file <<FILE
foo
bar
bar bar
foo foo
FILE
<
passes the contents of a file to a command's standard input.
$ cat < /etc/fstab
/dev/sda2 /boot ext4 nosuid,noexec,nodev,rw,noatime,nodiratime 0 2
/dev/sda4 / ext4 rw,noatime,nodiratime, 0 1
/dev/sdb5 /var ext4 nosuid,noexec,nodev,rw,relatime 0 2
...
Difference between “cat” and “cat <”
In the first case, cat opens the file, and in the second case, the shell opens the file, passing it as cat's standard input.
Technically, they could have different effects. For instance, it would be possible to have a shell implementation that was more (or less) privileged than the cat program. For that scenario, one might fail to open the file, while the other could.
That is not the usual scenario, but mentioned to point out that the shell and cat are not the same program.
for ((i=0; ; ++i )); do out=$(java PossibleReordering); if [[ $out == "(1,0)" || $out == "(0,0)" ]]; then echo "$i:$out"; fi; done
;
单分号,一般作为命令分隔符。可以将两个命令放在同一行。如: echo hello; echo there
,但真正写脚本的时候尽量不要这样写。
;;
双分号,用在 case条件语句的结束符。如:
case "$variable" in
abc) echo "\$variable = abc" ;;
xyz) echo "\$variable = xyz" ;;
esac
%
百分号,用于表示取余操作,也用于正则表达式。
~
波浪线,表示家目录,等价于$HOME
。如 cd ~
~+
表示当前工作目录,等价于$PWD
。
~-
表示上一个工作目录,等价于 ..
。
|
管道标识符,将上一个命令的输出作为下一个命令的输入,经常用到,没什么好说的。
>|
强制重定向。强制重写已经存在的文件。
||
表示逻辑或操作。
&
让命令在后台运行,例如 command &
一般用于启动后台进程服务。
&&
表示逻辑与操作。
*
星号,主要用于通配符匹配,当然也用于乘法表达式。
\
主要用于转义特殊字符,比如想转义双引号,可以这样 echo \" 输出 “
。
/
文件路径分隔符,比如 /opt/app/projects/
。当然也用作除法表达式。
.
点号,这个符号作用比较多。首先可以等价于 source
命令。也可以作为文件名字,在文件名开头,表示该文件是个隐藏文件。还可以表示当前目录, 比如拷贝某个文件到当前目录 cp /opt/app/a.md .
。如果是两个连续的点则表示上一级目录,比如 cd ..
。最后,点号也是正则表达式的元字符。
"
双引号,双引号里面的变量一般会被解析成赋值的内容。比如
name=frank
echo "hello $name" # hello frank
'
单引号,单引号里面的变量一般不会被解析,比如
name=frank
echo 'hello $name' #hello $name
``` 反引号(ESC键下面那个),要跟单引号区分开。反引号里面的内容会被当作指令执行,并将执行的结果赋值给变量。比如:
file=`ls ~`
echo $file #家目录下所有文件。
!
感叹号,一般用于取反。比如 !=
表示不等。
骚操作
在终端中执行,可以表示历史指令比如 !-3
,将会输出你刚刚输入的指令。但在脚本中不支持该种写法。
**
双星号,算术运算中表示求幂运算。比如
let "a=3**2"
echo $a #9
?
问号,表示条件测试;也用作三元运算符。也是正则表达式元字符。
$
美元符,放到变量前面,即引用一个变量的内容,比如:echo $PATH
;当然也是正则表达式的元字符。
${}
参数替换。用于在字符串中表示变量值。比如
name=frank
echo "hello ${name}" #hello frank
$*,$@
这两个特殊字符都用于获取传递给脚本的所有参数。当他们被双引号包围时,"$*"
会将所有的参数从整体上看做一份数据。而"$@"
仍然将每个参数都看作一份数据,彼此之间是独立的。
$#
表示参数个数。
$?
返回最近的一个命令或脚本的退出状态码,正确执行则返回0,否则返回非0。
$$
双美元符, 返回当前脚本的进程号。
()
小括号,命令组,一组圆括号括起来的命令代表一个命令组,以子shell方式运行。同时小括号里面的的变量类似局部变量,外部不能访问。比如
a=123
( a=321; )
echo "a = $a" # a = 123
还可以用于数组初始化。例如
arr=(ele1 ele2 ele3)
echo ${arr[1]} # ele2
{xxx,yyy,zzz}
有人叫花括号扩展,我举几个例子,可能大家就明白了。注意不能有空格。
echo {a,b,c}-{d,e,f} # a-d a-e a-f b-d b-e b-f c-d c-e c-f
cat {file1,file2,file3} > merge_file #将三个file的内容一同输入merge_file
cp file.{txt,backup} #拷贝file.txt成file.backup
{a..z}
跟上面类似,还是看例子吧。
echo {a..z} # a b c d e f g h i j k l m n o p q r s t u v w x y z
echo {0..3} # 0 1 2 3
{}
花括号,表示代码块 ,也用于表示匿名函数。也可以结合重定向符使用。例如:
fileline=~/a.txt
{
read line1
read line2
} < $fileline
echo $line1
echo $lien2
会将a.txt的前两行内容赋值给变量line1和line2;
骚操作
在xargs -i
中,还可以作为文本的占位符,用来标记输出文本的位置。
比如 ls *.txt | xargs -i -t cp {} {}.bak
会把所有txt文件拷贝一份,命名成txt.bak
{} \;
表示路径名字。一般跟find命令一起使用。例如 find . -name "*.sh" -exec rm {} \;
找出所有sh脚本,然后删除。注意{}
和 ``` 之间的空格,分号必须存在。
[]
中括号,用于在里面写判断表达式。也可以当作数组用。当然也是正则表达式元字符。
[[]]
双中括号,也用于在里面写判断表达式,比上面的中括号更灵活。
$[]
计算整数表达式,已经不推荐使用。例如
a=3;
b=7;
echo $[$a+$b] # 10
(())
双小括号, 计算整数表达式,推荐使用。如
a = 23
(( a++ ))
echo "a (after a++) = $a" # 24
>
,&>
, >&
, >>
这四个都是重定向符,分别举例说明。
cat ~/a.txt >a.log
将文件a.txt的内容输出到文件a.log中去,如果文件存在则覆盖;
command &>filename
重定向command的标准输出(stdout)和标准错误(stderr)到文件filename中,一般用于启动进程脚本;
command >&2
把command的标准输出(stdout)重定向到标准错误(stderr)中;
cat ~/a.txt >> a.log
把a.txt的输出以追加得方式输入到文件a.log中,如果文件不存在则创建。
-
短横线,可用于参数选择 例如 ls -al
。也可以表示上一个工作目录,例如 cd -
。当然也是数学运算符,用于表示减法操作。
=
等号,数学运算符,赋值操作。例如
a=28
echo $a
也可以用于表示比较操作,例如,if [ "$a" = "$b" ]
注意等号左右两侧要有空格。
#
井号,一般用于注释语句前面,表示该条语句是注释。也是正则表达式的元字符。
注意: 脚本的第一行#!/bin/bash 不作为注释,在双引号或者单引号以及转义字符之后的也不会作为注释符使用。例如
echo "The # here does not begin a comment."
echo 'The # here does not begin a comment.'
echo The \# here does not begin a comment.
骚操作 可以做进制转换,例如
echo $((2#101)) #5
echo $((8#101)) #65
echo $((10#101)) #10 1
,
逗号,用于连接一连串的数学表达式,这串数学表达式均被求值,但只有最后一个求值结果被返回。例如:
# Set "a = 9" and "t2 = 15 / 3"
let "t2 = ((a = 9, 15 / 3))"
也可以用于连接字符串,比如 echo {a,b}/test
输出 a/test
b/test
骚操作 用在变量引用中,表示首字母小写,如果是两个逗号,则表示全部小写。例如
a="AFrank"
echo ${a,} #aFrank
echo ${a,,} #afrank
+
数学运算符,表示加操作。也是正则表达式元字符。
骚操作
用于设置变量值。使用方式 ${parameter+alt_value}
如果变量 parameter 设置了值,则使用 alt_value 的值,否则使用空字符。
举个例子,感受一下
#param1 not set
a=${param1+xyz}
echo "a = $a" # a =
#parma2 set null
param2=
a=${param2+xyz}
echo "a = $a" # a = xyz
param3=123
a=${param3+xyz}
echo "a = $a" # a = xyz
注意 配合冒号使用时会有不同。举个例子,继续感受一下
a=${param4:+xyz}
echo "a = $a" # a =
param5=
a=${param5:+xyz}
echo "a = $a" # a =
#Different result from a=${param5+xyz}
param6=123
a=${param6:+xyz}
echo "a = $a" # a = xyz
^
用于正则表达式。
骚操作 用于大小写转化。看下面的例子。
var=hellFrank
echo ${var^} # HelloFrank
echo ${var^^} # HELLOFRANK
<<
双小于号,称作 here-doc
。一般用于给命令提供输入多行内容。比如
tr a-z A-Z <<EOF
> one
> two
> three
> EOF
输出: ONE TWO THREE
默认的,here doc
里面的变量会进行替换。比如
cat << EOF
> Working dir $PWD
> EOF
输出:Working dir /home/frank
如果给here doc 标识符加上双引号或者单引号则会禁止变量替换。比如
cat << "EOF"
> Working dir $PWD
> EOF
输出:Working dir $PWD
骚操作
再 <<后面添加-,可以忽略TAB空白字符。比如
tr a-z A-Z <<-EOF
> one
> two
> three
> EOF
输出: ONE TWO THREE
<<<
三个小于号,称作here string
,here doc
的变种。比here doc
更灵活。例如
tr a-z A-Z <<<"Yes it is a string" # YES IT IS A STRING
name=frank
# 双引号里面会解析变量
tr a-z A-Z <<<"Yes i'm $name" # YES I'M FRANK
# 单引号里面不解析变量
tr a-z A-Z <<<'Yes i\'m $name' # YES I'M $NAME
:
冒号,表示空,什么都不做,但是有返回值,返回值为0(即true)
例如:: ; echo $? 输出0。$?
的意思就是返回上条指令的状态。
利用此特性可以作为 while 的无限循环条件,也可以作为 if 分支的占位符。
比如
while : #same as while true
do
operation-1
operation-2
...
operation-n
done
或者
if condition
then : # Do nothing and branch ahead
else # Or else ...
take-some-action
fi
除此之外还可以结合重定向符号使用,将文件内容清空,但是不改变文件权限,如果不存在则会自动创建。
1: > data.xxx # File "data.xxx" now empty.
等价于 cat /dev/null >data.xxx
如果以追加方式的重定向,则对文件不构成任何修改。同样如果文件不存在也会新建一个。例如 : >> data.xxx
。
注意 这个只能在普通文件中使用,不能在管道,符号链接和其他特殊文件中使用;
你也可以作为域分隔符,比如环境变量$PATH
中,或者passwd
中,都有冒号的作为域分隔符的存在;例如
usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/games
骚操作 设置默认值,如果param没有设置,则使用默认值,例如
parma=frank
echo ${param:=default} #frank
echo ${test:=default} #default
你也可以将冒号作为函数名,不过这个会将冒号的本来意义转变,所以不要这么搞。
:()
{
echo "The name of this function is colon"
}
Bash 是一个为 GNU 计划编写的 Unix shell,是 Linux 和 Mac OS X 下的默认 shell。 以下大多数例子可以作为脚本的一部分运行,也可直接在 shell 下交互执行。
#!/bin/bash
# 脚本的第一行叫 shebang,用来告知系统如何执行该脚本:
# 参见: http://en.wikipedia.org/wiki/Shebang_(Unix)
# 如你所见,注释以 # 开头,shebang 也是注释。
# 显示 “Hello world!”
echo Hello world!
# 每一句指令以换行或分号隔开:
echo 'This is the first line'; echo 'This is the second line'
# 声明一个变量:
Variable="Some string"
# 下面是错误的做法:
Variable = "Some string"
# Bash 会把 Variable 当做一个指令,由于找不到该指令,因此这里会报错。
# 也不可以这样:
Variable= 'Some string'
# Bash 会认为 'Some string' 是一条指令,由于找不到该指令,这里再次报错。
# (这个例子中 'Variable=' 这部分会被当作仅对 'Some string' 起作用的赋值。)
# 使用变量:
echo $Variable
echo "$Variable"
echo '$Variable'
# 当你赋值 (assign) 、导出 (export),或者以其他方式使用变量时,变量名前不加 $。
# 如果要使用变量的值, 则要加 $。
# 注意: ' (单引号) 不会展开变量(即会屏蔽掉变量)。
# 在变量内部进行字符串代换
echo ${Variable/Some/A}
# 会把 Variable 中首次出现的 "some" 替换成 “A”。
# 变量的截取
Length=7
echo ${Variable:0:Length}
# 这样会仅返回变量值的前7个字符
# 变量的默认值
echo ${Foo:-"DefaultValueIfFooIsMissingOrEmpty"}
# 对 null (Foo=) 和空串 (Foo="") 起作用; 零(Foo=0)时返回0
# 注意这仅返回默认值而不是改变变量的值
# 内置变量:
# 下面的内置变量很有用
echo "Last program return value: $?"
echo "Script's PID: $$"
echo "Number of arguments: $#"
echo "Scripts arguments: $@"
echo "Scripts arguments separated in different variables: $1 $2..."
# 读取输入:
echo "What's your name?"
read Name # 这里不需要声明新变量
echo Hello, $Name!
# 通常的 if 结构看起来像这样:
# 'man test' 可查看更多的信息
if [ $Name -ne $USER ]
then
echo "Your name isn't your username"
else
echo "Your name is your username"
fi
# 根据上一个指令执行结果决定是否执行下一个指令
echo "Always executed" || echo "Only executed if first command fails"
echo "Always executed" && echo "Only executed if first command does NOT fail"
# 在 if 语句中使用 && 和 || 需要多对方括号
if [ $Name == "Steve" ] && [ $Age -eq 15 ]
then
echo "This will run if $Name is Steve AND $Age is 15."
fi
if [ $Name == "Daniya" ] || [ $Name == "Zach" ]
then
echo "This will run if $Name is Daniya OR Zach."
fi
# 表达式的格式如下:
echo $(( 10 + 5 ))
# 与其他编程语言不同的是,bash 运行时依赖上下文。比如,使用 ls 时,列出当前目录。
ls
# 指令可以带有选项:
ls -l # 列出文件和目录的详细信息
# 前一个指令的输出可以当作后一个指令的输入。grep 用来匹配字符串。
# 用下面的指令列出当前目录下所有的 txt 文件:
ls -l | grep "\.txt"
# 重定向输入和输出(标准输入,标准输出,标准错误)。
# 以 ^EOF$ 作为结束标记从标准输入读取数据并覆盖 hello.py :
cat > hello.py << EOF
#!/usr/bin/env python
from __future__ import print_function
import sys
print("#stdout", file=sys.stdout)
print("#stderr", file=sys.stderr)
for line in sys.stdin:
print(line, file=sys.stdout)
EOF
# 重定向可以到输出,输入和错误输出。
python hello.py < "input.in"
python hello.py > "output.out"
python hello.py 2> "error.err"
python hello.py > "output-and-error.log" 2>&1
python hello.py > /dev/null 2>&1
# > 会覆盖已存在的文件, >> 会以累加的方式输出文件中。
python hello.py >> "output.out" 2>> "error.err"
# 覆盖 output.out , 追加 error.err 并统计行数
info bash 'Basic Shell Features' 'Redirections' > output.out 2>> error.err
wc -l output.out error.err
# 运行指令并打印文件描述符 (比如 /dev/fd/123)
# 具体可查看: man fd
echo <(echo "#helloworld")
# 以 "#helloworld" 覆盖 output.out:
cat > output.out <(echo "#helloworld")
echo "#helloworld" > output.out
echo "#helloworld" | cat > output.out
echo "#helloworld" | tee output.out >/dev/null
# 清理临时文件并显示详情(增加 '-i' 选项启用交互模式)
rm -v output.out error.err output-and-error.log
# 一个指令可用 $( ) 嵌套在另一个指令内部:
# 以下的指令会打印当前目录下的目录和文件总数
echo "There are $(ls | wc -l) items here."
# 反引号 `` 起相同作用,但不允许嵌套
# 优先使用 $( ).
echo "There are `ls | wc -l` items here."
# Bash 的 case 语句与 Java 和 C++ 中的 switch 语句类似:
case "$Variable" in
# 列出需要匹配的字符串
0) echo "There is a zero.";;
1) echo "There is a one.";;
*) echo "It is not null.";;
esac
# 循环遍历给定的参数序列:
# 变量$Variable 的值会被打印 3 次。
for Variable in {1..3}
do
echo "$Variable"
done
# 或传统的 “for循环” :
for ((a=1; a <= 3; a++))
do
echo $a
done
# 也可以用于文件
# 用 cat 输出 file1 和 file2 内容
for Variable in file1 file2
do
cat "$Variable"
done
# 或作用于其他命令的输出
# 对 ls 输出的文件执行 cat 指令。
for Output in $(ls)
do
cat "$Output"
done
# while 循环:
while [ true ]
do
echo "loop body here..."
break
done
# 你也可以使用函数
# 定义函数:
function foo ()
{
echo "Arguments work just like script arguments: $@"
echo "And: $1 $2..."
echo "This is a function"
return 0
}
# 更简单的方法
bar ()
{
echo "Another way to declare functions!"
return 0
}
# 调用函数
foo "My name is" $Name
# 有很多有用的指令需要学习:
# 打印 file.txt 的最后 10 行
tail -n 10 file.txt
# 打印 file.txt 的前 10 行
head -n 10 file.txt
# 将 file.txt 按行排序
sort file.txt
# 报告或忽略重复的行,用选项 -d 打印重复的行
uniq -d file.txt
# 打印每行中 ',' 之前内容
cut -d ',' -f 1 file.txt
# 将 file.txt 文件所有 'okay' 替换为 'great', (兼容正则表达式)
sed -i 's/okay/great/g' file.txt
# 将 file.txt 中匹配正则的行打印到标准输出
# 这里打印以 "foo" 开头, "bar" 结尾的行
grep "^foo.*bar$" file.txt
# 使用选项 "-c" 统计行数
grep -c "^foo.*bar$" file.txt
# 如果只是要按字面形式搜索字符串而不是按正则表达式,使用 fgrep (或 grep -F)
fgrep "^foo.*bar$" file.txt
# 以 bash 内建的 'help' 指令阅读 Bash 自带文档:
help
help help
help for
help return
help source
help .
# 用 man 指令阅读相关的 Bash 手册
apropos bash
man 1 bash
man bash
# 用 info 指令查阅命令的 info 文档 (info 中按 ? 显示帮助信息)
apropos info | grep '^info.*('
man info
info info
info 5 info
# 阅读 Bash 的 info 文档:
info bash
info bash 'Bash Features'
info bash 6
info --apropos bash
本文使用typora从博客拷贝黏贴成markdown形式,typora真香。
sudo !!
user@host: cat /var/log/messages
cat /var/log/messages: Permission denied.
Don’t type: Up. Left. Left. Left…. sudo Enter. Eugh. Instead: sudo !!
This is a little shortcut works, because !! is a shell place holder for the last command executed.
Alt+.
Don’t type; mkdir MyNewDirectory; cd MyNewDirectory Instead;
mkdir MyNewDirectory
cd <Alt+.>
Ctrl+R
What was that command I ran? Up. Up. Up. Up. Oh there it is.
You search through your history one step at a time because you don’t know any better. What if I told you… there was a search!;
Don’t type: Up. Up. Up. Enter.
Instead: Ctrl+R
Simply tap Ctrl+R, and type the first few letters of the command you wanted. If the search doesn’t match on the first result, just tap Ctrl+R a few more times to scroll through results — shown below searching just on cat
.
cd
You would be amazed how many people don’t know this. cd
. That’s right. Without any arguments, it takes you back to your home directory.
cd -
Sometimes the simplist things are the best. Where you in the /var/www/foo
directory, but are now in /etc
? Simply cd -
will take you back to /var/www/foo
.
Don’t type: cd /var/www/foo
Instead: cd -
This might take some getting used to, but when you get the hang of it you’ll never go back. Let’s say you are editing a file in vim
(well, you wouldn’t use nano
, would you?!), and now you want to go and look in the /var/www/html
directory. You could quit vim, browse to the directory, only to find that you want to edit the file again. Instead, you can send vim to the background and come back to it later.
Type: Ctrl+Z — This is a shortcut that backgrounds any existing foreground task. Useful for, but not limited to; less
, cat
, man
, vim
, etc.
Where did my foreground task go, you might ask. Simply type jobs
to see it in a list.
user@host: jobs
[1] Stopped vim
Great. You can now go do something else. whenever you want this back again, simply, type fg
. This will bring the background job (vim) back again. Note that the process is paused, so if you’re running something like tail
on a file, the process will have some catching up to do. If you have multiple jobs running in the background fg 3
, for example, will resume the 3rd job. Don’t forget to run the jobs
command to see a list.
netstatx
If you run a command with the same arguments nearly all the time, create a “shortcut” alias for this — I have many of them. I often use the x
syntax — ie, the command’s normal name followed by an x. For example, with netstat
, I always run it with -n
(numeric addresses only) , -t
(tcp protocol), -a
(all), -u
(udp protocol), and -e
(extended output). netstat -ntaupe
— it rolls right off the tounge. I’m lazy though (and might forget an option), so I aliased that to netstatx
like this;
alias netstatx="netstat -ntaupe"
Try it for anything you run regularly.
Don’t type: netstat -ntaupe
Instead: netstatx
(or whatever command you use often!
#!/usr/bin/env bash
set -Eeuo pipefail
trap cleanup SIGINT SIGTERM ERR EXIT
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)
usage() {
cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [-f] -p param_value arg1 [arg2...]
Script description here.
Available options:
-h, --help Print this help and exit
-v, --verbose Print script debug info
-f, --flag Some flag description
-p, --param Some param description
EOF
exit
}
cleanup() {
trap - SIGINT SIGTERM ERR EXIT
# script cleanup here
}
setup_colors() {
if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
else
NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''
fi
}
msg() {
echo >&2 -e "${1-}"
}
die() {
local msg=$1
local code=${2-1} # default exit status 1
msg "$msg"
exit "$code"
}
parse_params() {
# default values of variables set from params
flag=0
param=''
while :; do
case "${1-}" in
-h | --help) usage ;;
-v | --verbose) set -x ;;
--no-color) NO_COLOR=1 ;;
-f | --flag) flag=1 ;; # example flag
-p | --param) # example named parameter
param="${2-}"
shift
;;
-?*) die "Unknown option: $1" ;;
*) break ;;
esac
shift
done
args=("$@")
# check required params and arguments
[[ -z "${param-}" ]] && die "Missing required parameter: param"
[[ ${#args[@]} -eq 0 ]] && die "Missing script arguments"
return 0
}
parse_params "$@"
setup_colors
# script logic here
msg "${RED}Read parameters:${NOFORMAT}"
msg "- flag: ${flag}"
msg "- param: ${param}"
msg "- arguments: ${args[*]-}"
Since your question is about using any other arguments, here are some useful ones:
!^ first argument
!$ last argument
!* all arguments
!:2 second argument
!:2-3 second to third arguments
!:2-$ second to last arguments
!:2* second to last arguments
!:2- second to next to last arguments
!:0 the command
!! repeat the previous line
The first four forms are more often used. The form !:2- is somewhat counter-intuitive, as it doesn't include the last argument.
My Minimal, Safe Bash Script Template
https://github.com/leogtzr/minimal-safe-bash-template
#!/usr/bin/env bash
#: Your comments here.
set -o errexit
set -o nounset
set -o pipefail
work_dir=$(dirname "$(readlink --canonicalize-existing "${0}" 2> /dev/null)")
readonly conf_file="${work_dir}/script.conf"
readonly error_reading_conf_file=80
readonly error_parsing_options=81
readonly script_name="${0##*/}"
a_option_flag=0
abc_option_flag=0
flag_option_flag=0
trap clean_up ERR EXIT SIGINT SIGTERM
usage() {
cat <<USAGE_TEXT
Usage: ${script_name} [-h | --help] [-a <ARG>] [--abc <ARG>] [-f | --flag]
DESCRIPTION
Your description here.
OPTIONS:
-h, --help
Print this help and exit.
-f, --flag
Description for flag option.
-a
Description for the -a option.
--abc
Description for the --abc option.
USAGE_TEXT
}
clean_up() {
trap - ERR EXIT SIGINT SIGTERM
# Remove temporary files/directories, log files or rollback changes.
}
die() {
local -r msg="${1}"
local -r code="${2:-90}"
echo "${msg}" >&2
exit "${code}"
}
if [[ ! -f "${conf_file}" ]]; then
die "error reading configuration file: ${conf_file}" "${error_reading_conf_file}"
fi
# shellcheck source=script.conf
. "${conf_file}"
parse_user_options() {
local -r args=("${@}")
local opts
# The following code works perfectly for
opts=$(getopt --options a:,f,h --long abc:,help,flag -- "${args[@]}" 2> /dev/null) || {
usage
die "error: parsing options" "${error_parsing_options}"
}
eval set -- "${opts}"
while true; do
case "${1}" in
--abc)
abc_option_flag=1
readonly abc_arg="${2}"
shift
shift
;;
-a)
a_option_flag=1
readonly a_arg="${2}"
shift
shift
;;
--help|-h)
usage
exit 0
shift
;;
--flag|-f)
flag_option_flag=1
shift
;;
--)
shift
break
;;
*)
break
;;
esac
done
}
parse_user_options "${@}"
if ((flag_option_flag)); then
echo "flag option set"
fi
if ((abc_option_flag)); then # Check if the flag options are set or ON:
# Logic for when --abc is set.
# "${abc_arg}" should also be set.
echo "Using --abc option -> arg: [${abc_arg}]"
fi
if ((a_option_flag)); then
# Logic for when -a is set.
# "${a_arg}" should also be set.
echo "Using -a option -> arg: [${a_arg}]"
fi
exit 0
https://unix.stackexchange.com/questions/122845/using-a-b-for-variable-assignment-in-scripts
# | expr | parameter Set and Not Null | parameter Set But Null | parameter Unset |
---|---|---|---|---|
1 | \${parameter:-word} | substitute parameter | substitute word | substitute word |
2 | \${parameter-word} | substitute parameter | substitute null | substitute word |
3 | \${parameter:=word} | substitute parameter | assign word | assign word |
4 | \${parameter=word} | substitute parameter | substitute null | assign word |
5 | \${parameter:?word} | substitute parameter | error, exit | error, exit |
6 | \${parameter?word} | substitute parameter | substitute null | error, exit |
7 | \${parameter:+word} | substitute word | substitute null | substitute null |
8 | \${parameter+word} | substitute word | substitute word | substitute null |
#!/bin/bash
echo "1. 替换变量值:${var}"
echo "2. 变量为空时替换变量值,变量本身不改变:${var:-word}"
echo "3. 变量为空时替换变量值,变量本身被改变:${var:=word}"
echo "4. 变量为空时替换变量值,打印错误消息,用于检查变量是否正确设置:${var:?message}"
echo "5. 变量不为空时替换变量值,变量本身不改变:${var:+word2}"
echo "6. 可使用使用转义符来转移:$\{var}"
# 打印 a
echo "${parameter:-a}"
# 打印 b
echo "${parameter:-b}"
# 打印 c parameter 本身为空时被赋值 c 了
echo "${parameter:=c}"
# 打印 c
echo "${parameter:=d}"
#!/bin/bash
JAIL="/nginx"
HINT=""
# Do three possibilities for $JAIL ##
for i in 1 2 3
do
case $i in
1) JAIL="/nginx/jail"; HINT="value set";;
2) JAIL=""; HINT="value set to empty string";;
3) unset JAIL; HINT="\$JAIL unset";;
esac
###############################################
# Update user with actual values and action
# $JAIL set to a non-empty string (1)
# $JAIL set to the empty string (2)
# $JAIL can be unset (3)
################################################
echo "*** Current value of \$JAIL is '$JAIL' ($HINT) ***"
## Determine if a bash variable is empty or not ##
if [ -z "${JAIL}" ]; then
echo "JAIL is unset or set to the empty string"
fi
if [ -z "${JAIL+set}" ]; then
echo "JAIL is unset"
fi
if [ -z "${JAIL-unset}" ]; then
echo "JAIL is set to the empty string"
fi
if [ -n "${JAIL}" ]; then
echo "JAIL is set to a non-empty string"
fi
if [ -n "${JAIL+set}" ]; then
echo "JAIL is set, possibly to the empty string"
fi
if [ -n "${JAIL-unset}" ]; then
echo "JAIL is either unset or set to a non-empty string"
fi
done
## syntax 1 ##
if [[ -z "$variable" ]]; then
echo "Empty $variable"
else
echo "Do whatever you want as \$variable is not empty"
fi
## Syntax 2 ##
[[ -z "$variable" ]] && echo "Empty" || echo "Not empty"
## Syntax 3 ##
[ -z "$var" ] && echo "Empty: Yes" || echo "Empty: No"
今天修理一下ctl的bash脚本,使得对于pid文件不存在时,还能根据可执行文件名去pgrep获得pid。 在此过程中,我又重新温习了一下bash函数知识,然后根据一篇写得很不错的博客Bash Functions整理了一下,代码如下(在线版本):