今天修理一下 ctl 的 bash 脚本,使得对于 pid 文件不存在时,还能根据可执行文件名去 pgrep 获得 pid,在此过程中,我又重新温习了一下 bash 函数知识,然后整理了一下,代码如下:
#!/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
$ 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.
$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1
$ a=$((2+3)); echo "a=$a"
a=5
$ ((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 ...
$ echo ${SHELL}
/bin/bash
$ 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
Shell 编程里面的奇葩字符
case "$variable" in
abc) echo "\$variable = abc" ;;
xyz) echo "\$variable = xyz" ;;
esac
name=frank
echo "hello $name" # hello frank
name=frank
echo 'hello $name' #hello $name
file=`ls ~`
echo $file #家目录下所有文件。
let "a=3**2"
echo $a #9
name=frank
echo "hello ${name}" #hello frank
a=123
( a=321; )
echo "a = $a" # a = 123 还可以用于数组初始化。例如
arr=(ele1 ele2 ele3)
echo ${arr[1]} # ele2
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
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;
a=3;
b=7;
echo $[$a+$b] # 10
a = 23
(( a++ ))
echo "a (after a++) = $a" # 24
a=28
echo $a 也可以用于表示比较操作,例如,if [ "$a" = "$b" ] 注意等号左右两侧要有空格。
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
#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
tr a-z A-Z <<EOF
> one
> two
> three
> EOF 输出:
cat << EOF
> Working dir $PWD
> EOF 输出:Working dir /home/frank
cat << "EOF"
> Working dir $PWD
> EOF 输出:Working dir $PWD
tr a-z A-Z <<-EOF
> one
> two
> three
> EOF 输出:
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
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 除此之外还可以结合重定向符号使用,将文件内容清空,但是不改变文件权限,如果不存在则会自动创建。
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
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.
Don’t type; mkdir MyNewDirectory; cd MyNewDirectory
Instead;
mkdir MyNewDirectory
cd <Alt+.>
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.
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.
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.
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.
#!/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
#exprparameter Set and Not Nullparameter Set But Nullparameter Unset1|||||
简单使用子进程的模式

#!/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"
