玩转一下 Bash
今天修理一下 ctl 的 bash 脚本,使得对于 pid 文件不存在时,还能根据可执行文件名去 pgrep 获得 pid,在此过程中,我又重新温习了一下 bash 函数知识,然后根据一篇写得很不错的博客 Bash Functions 整理了一下,代码如下:
#!/bin/bash # The passed parameters are $1, $2, $3 … $n, corresponding to the position of the parameter after the function’s name. # The $0 variable is reserved for the function’s name. # The $# variable holds the number of positional parameters/arguments passed to the function. # The $* or $@ variable holds all positional parameters/arguments passed to the function. function greeting () { # Local variables can be declared within the function body with # the local keyword and can be used only inside that function. # You can have local variables with the same name in different functions. local func_result="Hello $1" # In Bash all variables by default are defined as global, # even if declared inside the function. global_result="some result" # Another, better option to return a value from a function is # to send the value to stdout using echo or printf like shown below: echo "$func_result" # The return status can be specified by using # the return keyword and it is assigned to the variable $?. # The return statement terminates the function. # You can think of it as the function’s exit status. return 55 } # assigning to the func_result variable using the $() mechanism func_result=$(greeting 'Joe') # console: return:55 echo "return:$?" # console: func_result:Hello Joe echo "func_result:$func_result" # caution: the $() invoking will not effect the global_result # console: global_result: echo "global_result:$global_result" # console: Hello Joe greeting "Joe" # console: global_result:some result echo "global_result:$global_result"
Double parenthesis with and without dollar
- Parenthesis: () (plural parentheses)
- Brackets: [] (also called square brackets)
- Braces: {} (also called curly braces)
- Backticks: ``
- More
$(...) 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
Speed up your command line navigation
本文使用 typora 从博客拷贝黏贴成 markdown 形式,typora 真香。
Run that again as root — 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.
Re-type that last argument — Alt+.
Don’t type; mkdir MyNewDirectory; cd MyNewDirectory
Instead;
mkdir MyNewDirectory cd <Alt+.>
Search for that command I ran — 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
.
Go back to your home directory — 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.
Go back to the last 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 -
Job control — backgrounding, foreground, etc
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.
Alias the stuff you use frequently — eg 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!
bash 实现的数据结构: torokmark/utils.sh Complex Types for shell. String, Map, Set, Stack, Date, Random and others
- Minimal safe Bash script template - see the article with full description: betterdev.blog/minimal-safe-bash-script-template
- script-template.sh
#!/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[*]-}"
How to use arguments from previous command?
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
Bash Hacker 中的默认值语法
https://unix.stackexchange.com/questions/122845/using-a-b-for-variable-assignment-in-scripts
#exprparameter Set and Not Nullparameter Set But Nullparameter Unset1|||||
参考:
简单使用子进程的模式
How To Bash Shell Find Out If a Variable Is Empty Or Not
#!/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"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论