bingoohuang/blog

玩转一下Bash

bingoohuang opened this issue · 16 comments

今天修理一下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

  1. Definitions:

    • Parenthesis: () (plural parentheses)
    • Brackets: [] (also called square brackets)
    • Braces: {} (also called curly braces)
    • Backticks: ``
    • More
  2. $(...) 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

  3. (...) 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
  4. $((...)) means perform arithmetic and return the result of the calculation. Example:

    $ a=$((2+3)); echo "a=$a"
    a=5
  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 ...
  6. ${...} means return the value of the shell variable named in the braces. Example:

    $ echo ${SHELL}
    /bin/bash
  7. {...} 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.

  1. What does <<< mean?
  2. Difference between “cat” and “cat <”
for ((i=0; ; ++i )); do out=$(java PossibleReordering); if [[ $out == "(1,0)" ||  $out == "(0,0)" ]]; then echo "$i:$out";  fi; done

美团技术团队:Java内存访问重排序的研究

Shell编程里面的奇葩字符, 内容来源参考

  1. ; 单分号,一般作为命令分隔符。可以将两个命令放在同一行。如: echo hello; echo there,但真正写脚本的时候尽量不要这样写。

  2. ;; 双分号,用在 case条件语句的结束符。如:

    case "$variable" in
      abc)  echo "\$variable = abc" ;;
      xyz)  echo "\$variable = xyz" ;;
    esac
  3. % 百分号,用于表示取余操作,也用于正则表达式。

  4. ~ 波浪线,表示家目录,等价于$HOME。如 cd ~

  5. ~+ 表示当前工作目录,等价于$PWD

  6. ~- 表示上一个工作目录,等价于 ..

  7. | 管道标识符,将上一个命令的输出作为下一个命令的输入,经常用到,没什么好说的。

  8. >| 强制重定向。强制重写已经存在的文件。

  9. || 表示逻辑或操作。

  10. & 让命令在后台运行,例如 command & 一般用于启动后台进程服务。

  11. && 表示逻辑与操作。

  12. * 星号,主要用于通配符匹配,当然也用于乘法表达式。

  13. \ 主要用于转义特殊字符,比如想转义双引号,可以这样 echo \" 输出 “

  14. / 文件路径分隔符,比如 /opt/app/projects/ 。当然也用作除法表达式。

  15. . 点号,这个符号作用比较多。首先可以等价于 source 命令。也可以作为文件名字,在文件名开头,表示该文件是个隐藏文件。还可以表示当前目录, 比如拷贝某个文件到当前目录 cp /opt/app/a.md . 。如果是两个连续的点则表示上一级目录,比如 cd ..。最后,点号也是正则表达式的元字符。

  16. " 双引号,双引号里面的变量一般会被解析成赋值的内容。比如

    name=frank
    echo "hello $name" # hello frank
  17. ' 单引号,单引号里面的变量一般不会被解析,比如

    name=frank
    echo 'hello $name' #hello $name
  18. ``` 反引号(ESC键下面那个),要跟单引号区分开。反引号里面的内容会被当作指令执行,并将执行的结果赋值给变量。比如:

    file=`ls ~`
    echo $file #家目录下所有文件。
  19. ! 感叹号,一般用于取反。比如 != 表示不等。
    *操作
    在终端中执行,可以表示历史指令比如 !-3,将会输出你刚刚输入的指令。但在脚本中不支持该种写法。

  20. ** 双星号,算术运算中表示求幂运算。比如

    let "a=3**2"
    echo $a #9
  21. ? 问号,表示条件测试;也用作三元运算符。也是正则表达式元字符。

  22. $ 美元符,放到变量前面,即引用一个变量的内容,比如:echo $PATH;当然也是正则表达式的元字符。

  23. ${} 参数替换。用于在字符串中表示变量值。比如

    name=frank
    echo "hello ${name}" #hello frank
  24. $*,$@ 这两个特殊字符都用于获取传递给脚本的所有参数。当他们被双引号包围时,"$*"会将所有的参数从整体上看做一份数据。而"$@"仍然将每个参数都看作一份数据,彼此之间是独立的。

  25. $# 表示参数个数。

  26. $? 返回最近的一个命令或脚本的退出状态码,正确执行则返回0,否则返回非0。

  27. $$ 双美元符, 返回当前脚本的进程号。

  28. () 小括号,命令组,一组圆括号括起来的命令代表一个命令组,以子shell方式运行。同时小括号里面的的变量类似局部变量,外部不能访问。比如

    a=123
    ( a=321; )
    echo "a = $a"   # a = 123

    还可以用于数组初始化。例如

    arr=(ele1 ele2 ele3)
    echo ${arr[1]} # ele2
  29. {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
  30. {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
  31. {} 花括号,表示代码块 ,也用于表示匿名函数。也可以结合重定向符使用。例如:

    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

  32. {} \; 表示路径名字。一般跟find命令一起使用。例如 find . -name "*.sh" -exec rm {} \;找出所有sh脚本,然后删除。注意{} 和 ``` 之间的空格,分号必须存在。

  33. [] 中括号,用于在里面写判断表达式。也可以当作数组用。当然也是正则表达式元字符。

  34. [[]] 双中括号,也用于在里面写判断表达式,比上面的中括号更灵活。

  35. $[] 计算整数表达式,已经不推荐使用。例如

    a=3;
    b=7;
    echo $[$a+$b]   # 10
  36. (()) 双小括号, 计算整数表达式,推荐使用。如

    a = 23
    (( a++ ))
    echo "a (after a++) = $a"       # 24
  37. >&>>&>> 这四个都是重定向符,分别举例说明。

    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中,如果文件不存在则创建。

  38. - 短横线,可用于参数选择 例如 ls -al。也可以表示上一个工作目录,例如 cd -。当然也是数学运算符,用于表示减法操作。

  39. = 等号,数学运算符,赋值操作。例如

    a=28
    echo $a

    也可以用于表示比较操作,例如,if [ "$a" = "$b" ] 注意等号左右两侧要有空格。

  40. # 井号,一般用于注释语句前面,表示该条语句是注释。也是正则表达式的元字符。

    注意:
    脚本的第一行#!/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
  41. , 逗号,用于连接一连串的数学表达式,这串数学表达式均被求值,但只有最后一个求值结果被返回。例如:

    # 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
  42. + 数学运算符,表示加操作。也是正则表达式元字符。

    *操作
    用于设置变量值。使用方式 ${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
  43. ^ 用于正则表达式。

    *操作
    用于大小写转化。看下面的例子。

    var=hellFrank
    echo ${var^}           # HelloFrank
    echo ${var^^}          # HELLOFRANK
  44. << 双小于号,称作 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

  45. <<< 三个小于号,称作here stringhere 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
  46. : 冒号,表示空,什么都不做,但是有返回值,返回值为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"
    }

X分钟速成Y 其中 Y=bash

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真香。

Blog

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!

  1. Minimal safe Bash script template - see the article with full description: betterdev.blog/minimal-safe-bash-script-template
  2. 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

# 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}"
  1. 简单使用子进程的模式

image

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"