编写shell脚本(三)

for循环

for variable [in words]; do
    commands
done

花括号展开

for i in {A..Z}; do echo $i; done

路径名展开

root@ubuntu:/code# for i in *;do echo $i;done
12.txt
file.sh
foo.html
menu.sh
myfile1
o2.txt
output.txt
sys_info_page
test
test.txt
trouble.sh
新建文本文档.txt

命令替换

root@ubuntu:/code# wc -w
-rwxrw-r--  1 root root    0 Feb 21 19:53 新建文本文档.txt*
9
root@ubuntu:/code# wc -c
-rwxrw-r--  1 root root    0 Feb 21 19:53 新建文本文档.txt*
66
root@ubuntu:/code# wc -m
-rwxrw-r--  1 root root    0 Feb 21 19:53 新建文本文档.txt*
54
root@ubuntu:/code# wc -L
-rwxrw-r--  1 root root    0 Feb 21 19:53 新建文本文档.txt*
59

在这里插入图片描述
strings 指令把*给展开了,???

length=$(echo "$i" | wc -c)""也没有用了,$i已经是*展开后的单词了orz

而且分割出来的是以空格为分隔符的单词

# find longest string in a file
while [[ -n $1 ]]; do #字符串长度大于0
  if [[ -r $1 ]]; then #文件可读 
    max_len=0
    longest_word=
    for i in $(strings $1); do
      length=$(echo "$i" | wc -c)
echo "$i's length is $length"
      if [[ $length -gt $max_len ]]; then
	
        max_len=$length
        longest_word=$i
      fi

    done
    echo "$1: '$longest_word' ($max_len characters)"
  fi
  shift
done

如果for循环省略掉了words部分,会默认处理位置参数

for i; do
  if [[ -r $i ]]; then
    max_len=0
    longest_word=
    for j in $(strings $1); do
      length=$(echo $i | wc -w)
      if [[ $length -gt $max_len ]]; then
        max_len=$length
        longest_word=$i
      fi
    done
    echo "$i: '$longest_word' ($max_len characters)"
  fi
done

C语言格式

for (( expression1; expression2; expression3 )); do
    commands
done
(( expression1 ))
while (( expression2 )); do
    commands
    (( expression3 ))
done
for (( i = 0; i < 10; i++ )); do
  echo $i
done
0..9

在这里插入图片描述

report_home_space () {
    local format="%8s%10s%10s\n"
    local i dir_list total_files total_dirs total_size user_name
    if [[ $(id -u) -eq 0 ]]; then
        dir_list=/home/*
        user_name="All Users"
    else
        dir_list=$HOME
        user_name=$USER
    fi
    echo "<H2>Home Space Utilization ($user_name)</H2>"
    for i in $dir_list; do
        total_files=$(find $i -type f | wc -l)
        total_dirs=$(find $i -type d | wc -l)
        total_size=$(du -sh $i | cut -f 1)
        echo "<H3>$i</H3>"
        echo "<PRE>"
        printf "$format" "Dirs" "Files" "Size"
        printf "$format" "----" "-----" "----"
        printf "$format" $total_dirs $total_files $total_size
        echo "</PRE>"
    done
    return
}
  1. printf控制格式

字符串和数字

参数展开

基本参数

当 $a 展开后,会变成变量 a 所包含的值。可以用{}括起来,避免shell混淆

[me@linuxbox ~]$ a="foo"
[me@linuxbox ~]$ echo "$a_file
[me@linuxbox ~]$ echo "${a}_file"
foo_file

{}管理空变量展开
${parameter:-word}

root@ubuntu:/code# foo=
root@ubuntu:/code# echo ${foo:-"substitute value if unset"}
substitute value if unset
root@ubuntu:/code# echo $foo

root@ubuntu:/code# foo=bar
root@ubuntu:/code# echo ${foo:-"substitute value if unset"}
bar
root@ubuntu:/code# echo $foo
bar

${parameter:=word}
和上面那个的区别在于完成赋值,位置参数或其它的特殊参数不能以这种方式赋值。

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo:="default value if unset"}
default value if unset
[me@linuxbox ~]$ echo $foo
default value if unset

${parameter:?word} 会导致脚本带错退出,并发送Word到标准输出

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo:?"parameter is empty"}
bash: foo: parameter is empty
[me@linuxbox ~]$ echo $?
1
[me@linuxbox ~]$ foo=bar
[me@linuxbox ~]$ echo ${foo:?"parameter is empty"}
bar
[me@linuxbox ~]$ echo $?
0

${parameter:+word}
不为空才输出Word,但是foo不变化

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo:+"substitute value if set"}

[me@linuxbox ~]$ foo=bar
[me@linuxbox ~]$ echo ${foo:+"substitute value if set"}
substitute value if set

返回变量名的参数展开

${!prefix*}

${!prefix@}
所有以BASH开头的环境变量名

root@ubuntu:/code# echo ${!BASH*}
BASH BASHOPTS BASHPID BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND BASH_LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION
root@ubuntu:/code# echo ${!BASH@}
BASH BASHOPTS BASHPID BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND BASH_LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION

${#parameter}
展开成由 parameter 所包含的字符串的长度。通常,parameter 是一个字符串;然而,如果 parameter 是 @ 或者是 * 的话, 则展开结果是位置参数的个数。

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo "'$foo' is ${#foo} characters long."
'This string is long.' is 20 characters long.
root@ubuntu:/code# wc -c
This string is long.20

root@ubuntu:/code# wc -c
This string is long.
21

${parameter:offset}

${parameter:offset:length}
这些展开用来从 parameter 所包含的字符串中提取一部分字符。提取的字符始于 第 offset 个字符(从字符串开头算起)直到字符串的末尾,除非指定提取的长度

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo ${foo:5}
string is long.
[me@linuxbox ~]$ echo ${foo:5:6}
string

若 offset 的值为负数,则认为 offset 值是从字符串的末尾开始算起,而不是从开头。注意负数前面必须有一个空格, 为防止与 ${parameter:-word} 展开形式混淆。length,若出现,则必须不能小于零。
如果 parameter 是 @,展开结果是 length 个位置参数,从第 offset 个位置参数开始
${parameter#pattern}
${parameter##pattern}
这些展开会从 paramter 所包含的字符串中清除开头一部分文本,这些字符要匹配定义的 pattern。pattern 是 通配符模式,就如那些用在路径名展开中的模式。这两种形式的差异之处是该 # 形式清除最短的匹配结果, 而该 ## 模式清除最长的匹配结果。

[me@linuxbox ~]$ foo=file.txt.zip
[me@linuxbox ~]$ echo ${foo#*.}
txt.zip
[me@linuxbox ~]$ echo ${foo##*.}
zip

${parameter%pattern}

${parameter%%pattern}
这些展开和上面的 # 和 ## 展开一样,除了它们清除的文本从 parameter 所包含字符串的末尾开始,而不是开头。

[me@linuxbox ~]$ foo=file.txt.zip
[me@linuxbox ~]$ echo ${foo%.*}
file.txt
[me@linuxbox ~]$ echo ${foo%%.*}
file

${parameter/pattern/string}

${parameter//pattern/string}

${parameter/#pattern/string}

${parameter/%pattern/string}
这种形式的展开对 parameter 的内容执行查找和替换操作。如果找到了匹配通配符 pattern 的文本, 则用 string 的内容替换它。在正常形式下,只有第一个匹配项会被替换掉。在该 // 形式下,所有的匹配项都会被替换掉。 该 /# 要求匹配项出现在字符串的开头,而 /% 要求匹配项出现在字符串的末尾。/string 可能会省略掉,这样会 导致删除匹配的文本。

[me@linuxbox~]$ foo=JPG.JPG
[me@linuxbox ~]$ echo ${foo/JPG/jpg}
jpg.JPG
[me@linuxbox~]$ echo ${foo//JPG/jpg}
jpg.jpg
[me@linuxbox~]$ echo ${foo/#JPG/jpg}
jpg.JPG
[me@linuxbox~]$ echo ${foo/%JPG/jpg}
JPG.jpg

字符串操作展开可以用来替换其它常见命令比方说 sed 和 cut。 通过减少使用外部程序,展开提高了脚本的效率。

 ${#j} 取代命令 $(echo $j | wc -c) 
root@ubuntu:/code# time ./longest_word2.sh sys_info_page 
sys_info_page: 'format="%8s%10s%10s\n"' (22 characters)

real	0m0.005s
user	0m0.004s
sys	0m0.000s
root@ubuntu:/code# time ./longest_word.sh sys_info_page 
sys_info_page: 'format="%8s%10s%10s\n"' (23 characters)

real	0m0.425s
user	0m0.357s
sys	0m0.055s

  1. time命令比较脚本执行效率

大小写转换

那么大小写转换对什么有好处呢? 除了明显的审美价值,它在编程领域还有一个重要的角色。 让我们考虑一个数据库查询的案例。假设一个用户已经敲写了一个字符串到数据输入框中, 而我们想要在一个数据库中查找这个字符串。该用户输入的字符串有可能全是大写字母或全是小写或是两者的结合。 我们当然不希望把每个可能的大小写拼写排列填充到我们的数据库中。那怎么办?
解决这个问题的常见方法是规范化用户输入。也就是,在我们试图查询数据库之前,把用户的输入转换成标准化。 我们能做到这一点,通过把用户输入的字符全部转换成小写字母或大写字母,并且确保数据库中的条目 按同样的方式规范化。

#!/bin/bash
# ul-declare: demonstrate case conversion via declare
declare -u upper
declare -l lower
if [[ $1 ]]; then
    upper="$1"
    lower="$1"
    echo $upper
    echo $lower
fi

在上面的脚本中,我们使用 declare 命令来创建两个变量,upper 和 lower。我们把第一个命令行参数的值(位置参数1)赋给 每一个变量,然后把变量值在屏幕上显示出来:

[me@linuxbox ~]$ ul-declare aBc
ABC
abc

在这里插入图片描述

#!/bin/bash
# ul-param - demonstrate case conversion via parameter expansion
if [[ $1 ]]; then
    echo ${1,,}
    echo ${1,}
    echo ${1^^}
    echo ${1^}
fi

算数求值和展开

$((expression))

数基
在这里插入图片描述

[me@linuxbox ~]$ echo $((0xff))
255
[me@linuxbox ~]$ echo $((2#11111111))
255

在这里插入图片描述因为 shell 算术只操作整型,所以除法运算的结果总是整数:

[me@linuxbox ~]$ echo $(( 5 / 2 ))
2
#!/bin/bash
# modulo : demonstrate the modulo operator
for ((i = 0; i <= 20; i = i + 1)); do
    remainder=$((i % 5))
    if (( remainder == 0 )); then
        printf "<%d> " $i
    else
        printf "%d " $i
    fi
done
printf "\n"
<0> 1 2 3 4 <5> 6 7 8 9 <10> 11 12 13 14 <15> 16 17 18 19 <20>

执行赋值

[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo $foo
[me@linuxbox ~]$ if (( foo = 5 ));then echo "It is true."; fi
It is true.
[me@linuxbox ~]$ echo $foo
5
  1. 它把5赋值给变量 foo,
  2. 它计算测试条件为真,因为 foo 的值非零。

注意: 记住上面表达式中 = 符号的真正含义非常重要。单个 = 运算符执行赋值运算。foo = 5 是说“使得 foo 等于5”, 而 == 运算符计算等价性。foo == 5 是说“是否 foo 等于5?”。这会让人感到非常迷惑,因为 test 命令接受单个 = 运算符 来测试字符串等价性。这也是使用更现代的 [[ ]] 和 (( )) 复合命令来代替 test 命令的另一个原因。

在这里插入图片描述
++和C语言是一样的

for ((i = 0; i <= 20; ++i )); do
    if (((i % 5) == 0 )); then
        printf "<%d> " $i
    else
        printf "%d " $i
    fi
done

位运算符

在这里插入图片描述

[me@linuxbox ~]$ for ((i=0;i<8;++i)); do echo $((1<<i)); done
1
2
4
8
16
32
64
128

在这里插入图片描述

[me@linuxbox ~]$ if ((1)); then echo "true"; else echo "false"; fi
true
[me@linuxbox ~]$ if ((0)); then echo "true"; else echo "false"; fi
false

该 (( )) 复合命令把结果映射成 shell 正常的退出码:

[me@linuxbox~]$ a=0
[me@linuxbox~]$ ((a<1?++a:--a))
[me@linuxbox~]$ echo $a
1
[me@linuxbox~]$ ((a<1?++a:--a))
[me@linuxbox~]$ echo $a
0
me@linuxbox ~]$ a=0
[me@linuxbox ~]$ ((a<1?a+=1:a-=1))
bash: ((: a<1?a+=1:a-=1: attempted assignment to non-variable (error token is "-=1")
[me@linuxbox ~]$ ((a<1?(a+=1):(a-=1))) 加括号正确
#!/bin/bash
# arith-loop: script to demonstrate arithmetic operators
finished=0
a=0
printf "a\ta**2\ta**3\n"
printf "=\t====\t====\n"
until ((finished)); do
    b=$((a**2))
    c=$((a**3))
    printf "%d\t%d\t%d\n" $a $b $c
    ((a<10?++a:(finished=1)))
done
从0-10 

bc

我们已经看到 shell 是可以处理所有类型的整型算术的,但是如果我们需要执行更高级的数学运算或仅使用浮点数,该怎么办? 答案是,我们不能这样做。至少不能直接用 shell 完成此类运算。为此,我们需要使用外部程序。

数组

数组变量就像其它 bash 变量一样命名,当被访问的时候,它们会被自动地创建。这里是一个例子:

[me@linuxbox ~]$ a[1]=foo
[me@linuxbox ~]$ echo ${a[1]}
foo

也可以用 declare 命令创建一个数组:

[me@linuxbox ~]$ declare -a a

数组赋值

name[subscript]=value

多个值赋值使用下面的语法:

name=(value1 value2 ...)

这里的 name 是数组的名字,value… 是要按照顺序赋给数组的值,从元素0开始。例如,如果我们希望 把星期几的英文简写赋值给数组 days,我们可以这样做:

[me@linuxbox ~]$ days=(Sun Mon Tue Wed Thu Fri Sat)

还可以通过指定下标,把值赋给数组中的特定元素:

[me@linuxbox ~]$ days=([0]=Sun [1]=Mon [2]=Tue [3]=Wed [4]=Thu [5]=Fri [6]=Sat)

访问数组元素

root@ubuntu:/code# stat -c %y ./*
2020-02-21 19:47:39.494372088 +0800
2020-02-22 16:40:42.370569110 +0800
2020-02-21 19:52:46.388913302 +0800
2020-02-22 21:46:53.837524406 +0800
2020-02-22 21:46:01.999532665 +0800
2020-02-22 18:36:05.785471562 +0800
2020-02-21 20:11:19.751200530 +0800
2020-02-22 21:20:58.813742117 +0800
2020-02-22 21:20:50.413682007 +0800
2020-02-22 20:01:11.100704522 +0800
2020-02-22 21:20:45.945649925 +0800
2020-02-22 17:48:19.109333415 +0800
2020-02-22 15:33:04.453997026 +0800
2020-02-22 19:19:02.210234457 +0800
2020-02-21 19:53:52.214544423 +0800

-c定制输出格式 %y 最后修改时间(human-readable)

# hours : script to count files by modification time
function usage(){
  echo "usage : $(basename $0) directory"
}
if [[ ! -d $1 ]]; then
  usage
  exit
fi
count=0
#get the directory $1
for i in {0..23};do cnt[i]=0; done
for i in $(stat -c %y "$1"/* | cut -c 12-13); do
    j=${i/#0} # delete 0 in i
    ((cnt[j]++))
    ((count++))
done
echo -e "Hour\tFiles\tHour\tFiles"
echo  -e "----\t-----\t----\t-----"
for i in {0..11};do
  j=$((i+12))
  printf "%02d\t%d\t%02d\t%d\n" $i ${cnt[i]} $j ${cnt[j]}
done
printf "\nTotal files = %d\n" $count

如果不是目录,会显示脚本使用信息并退出。

root@ubuntu:/code# ./trouble.sh .
Hour	Files	Hour	Files
----	-----	----	-----
00	0	12	0
01	0	13	0
02	0	14	0
03	0	15	1
04	0	16	1
05	0	17	1
06	0	18	1
07	0	19	3
08	0	20	2
09	0	21	5
10	0	22	1
11	0	23	0

Total files = 15

数组操作

输出整个数组的内容

下标 * 和 @ 可以被用来访问数组中的每一个元素。与位置参数一样,@ 表示法在两者之中更有用处。 这里是一个演示:

[me@linuxbox ~]$ animals=("a dog" "a cat" "a fish")
[me@linuxbox ~]$ for i in ${animals[*]}; do echo $i; done
a
dog
a
cat
a
fish
[me@linuxbox ~]$ for i in ${animals[@]}; do echo $i; done
a
dog
a
cat
a
fish
[me@linuxbox ~]$ for i in "${animals[*]}"; do echo $i; done
a dog a cat a fish
[me@linuxbox ~]$ for i in "${animals[@]}"; do echo $i; done
a dog
a cat
a fish

确定数组元素个数

使用参数展开,我们能够确定数组元素的个数,与计算字符串长度的方式几乎相同。这里是一个例子:

[me@linuxbox ~]$ a[100]=foo
[me@linuxbox ~]$ echo ${#a[@]} # number of array elements
1
[me@linuxbox ~]$ echo ${#a[100]} # length of element 100
3

找到数组使用的下标

${!array[*]}

${!array[@]}
[me@linuxbox ~]$ foo=([2]=a [4]=b [6]=c)
[me@linuxbox ~]$ for i in "${foo[@]}"; do echo $i; done
a
b
c
[me@linuxbox ~]$ for i in "${!foo[@]}"; do echo $i; done
2
4
6

在数组末尾添加元素

[me@linuxbox~]$ foo=(a b c)
[me@linuxbox~]$ echo ${foo[@]}
a b c
[me@linuxbox~]$ foo+=(d e f)
[me@linuxbox~]$ echo ${foo[@]}
a b c d e f

数组排序

#!/bin/bash
# array-sort : Sort an array
a=(f e d c b a)
echo "Original array: ${a[@]}"
a_sorted=($(for i in "${a[@]}"; do echo $i; done | sort))
echo "Sorted array: ${a_sorted[@]}"
[me@linuxbox ~]$ array-sort
Original array: f e d c b a
Sorted array:
a b c d e f

删除数组

[me@linuxbox ~]$ foo=(a b c d e f)
[me@linuxbox ~]$ echo ${foo[@]}
a b c d e f
[me@linuxbox ~]$ unset foo
[me@linuxbox ~]$ echo ${foo[@]}
[me@linuxbox ~]$
也可以使用 unset 命令删除单个的数组元素:

[me@linuxbox~]$ foo=(a b c d e f)
[me@linuxbox~]$ echo ${foo[@]}
a b c d e f
[me@linuxbox~]$ unset 'foo[2]'
[me@linuxbox~]$ echo ${foo[@]}
a b d e f

数组下标开始于0,而不是1
有趣地是,给一个数组赋空值不会清空数组内容:

[me@linuxbox ~]$ foo=(a b c d e f)
[me@linuxbox ~]$ foo=
[me@linuxbox ~]$ echo ${foo[@]}
b c d e f

任何没有下标的对数组变量的引用都指向数组元素0:

[me@linuxbox~]$ foo=(a b c d e f)
[me@linuxbox~]$ echo ${foo[@]}
a b c d e f
[me@linuxbox~]$ foo=A
[me@linuxbox~]$ echo ${foo[@]}
A b c d e f

关联数组

关联数组使用字符串而不是整数作为数组索引。

declare -A colors
colors["red"]="#ff0000"
colors["green"]="#00ff00"
colors["blue"]="#0000ff"

Unlike integer indexed arrays, which are created by merely referencing them, associative arrays must be created with the declare command using the new -A option.
访问关联数组元素的方式几乎与整数索引数组相同:

echo ${colors[“blue”]}

最后

组命令和子 shell。bash 允许把命令组合在一起。可以通过两种方式完成;要么用一个 group 命令,要么用一个子 shell。 这里是每种方式的语法示例:

组命令 { command1; command2; [command3; ...] }
子shell (command1; command2; [command3;...])

这两种形式的不同之处在于,组命令用花括号把它的命令包裹起来,而子 shell 用括号。值得注意的是,鉴于 bash 实现组命令的方式, 花括号与命令之间必须有一个空格,并且最后一个命令必须用一个分号或者一个换行符终止。

那么组命令和子 shell 命令对什么有好处呢? 尽管它们有一个很重要的差异(我们马上会接触到),但它们都是用来管理重定向的。 让我们考虑一个对多个命令执行重定向的脚本片段。

ls -l > output.txt
echo "Listing of foo.txt" >> output.txt
cat foo.txt >> output.txt

{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt
(ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt
  %U   user name of owner
   %G   group name of owner

进程替换

虽然组命令和子 shell 看起来相似,并且它们都能用来在重定向中合并流,但是两者之间有一个很重要的不同之处。 然而,一个组命令在当前 shell 中执行它的所有命令,而一个子 shell(顾名思义)在当前 shell 的一个 子副本中执行它的命令。这意味着运行环境被复制给了一个新的 shell 实例。当这个子 shell 退出时,环境副本会消失, 所以在子 shell 环境(包括变量赋值)中的任何更改也会消失。因此,在大多数情况下,除非脚本要求一个子 shell, 组命令比子 shell 更受欢迎。组命令运行很快并且占用的内存也少。
一种适用于产生标准输出的进程:<(list)
另一种适用于接受标准输入的进程:>(list)
这里的 list 是一串命令列表:

root@ubuntu:/code# read < <(echo "foo")
root@ubuntu:/code# echo $REPLY
foo

root@ubuntu:/code#  echo <(echo "foo")
/dev/fd/63
<(不能有空格
root@ubuntu:/code# read <<(echo "fo1o")
bash: syntax error near unexpected token `(' 
<<也不能放在一起
进程替换经常被包含 read 命令的循环用到。这里是一个 read 循环的例子,处理一个目录列表的内容,内容创建于一个子 shell:

#!/bin/bash
# pro-sub : demo of process substitution
while read attr links owner group size date time filename; do
    cat <<- EOF
        Filename:     $filename
        Size:         $size
        Owner:        $owner
        Group:        $group
        Modified:     $date $time
        Links:        $links
        Attributes:   $attr
    EOF
done < <(ls -l --time-style '+%Y/%m/%d %H:%M:%S' | tail -n +2)

系统里默认的日期是Feb 22分成了两个单词

是为了消除列表的第一行文本,这行文本是多余的。

 -n, --lines=[+]NUM       output the last NUM lines, instead of the last 10;
                             or use -n +NUM to output starting with line NUM

trap

# trap-demo : simple signal handling demo
trap "echo 'I am ignoring you.'" SIGINT SIGTERM
for i in {1..5}; do
    echo "Iteration $i of 5"
    sleep 5
done
root@ubuntu:/code# ./trouble.sh 
iteration 0 of 5
^CI am ignoring you.
iteration 1 of 5
^CI am ignoring you.
iteration 2 of 5
^CI am ignoring you.
iteration 3 of 5

  1. echo被引号包围
# trap-demo2 : simple signal handling demo
exit_on_signal_SIGINT () {
    echo "Script interrupted." 2>&1
    exit 0
}
exit_on_signal_SIGTERM () {
    echo "Script terminated." 2>&1
    exit 0
}
trap exit_on_signal_SIGINT SIGINT
trap exit_on_signal_SIGTERM SIGTERM
for i in {1..5}; do
    echo "Iteration $i of 5"
    sleep 5
done

临时文件命名

tempfile=/tmp/$(basename $0).$$.$RANDOM

This will create a filename consisting of the program’s name, followed by its process ID (PID), followed by a random integer.
$RANDOM shell variable only returns a value in the range of 1-32767

tempfile=$(mktemp /tmp/foobar.$$.XXXXXXXXXX)

The mktemp program accepts a template as an argument that is used to build the filename. The template should include a series of “X” characters, which are replaced by a corresponding number of random letters and numbers.

/tmp/foobar.6593.UOZuvM6654

For scripts that are executed by regular users, it may be wise to avoid the use of the /tmp directory and create a directory for temporary files within the user’s home directory, with a line of code such as this:[[ -d $HOME/tmp ]] || mkdir $HOME/tmp

异步执行

父脚本

#!/bin/bash
# async-parent : Asynchronous execution demo (parent)
echo "Parent: starting..."
echo "Parent: launching child script..."
./async-child &  #./ is  necessary
pid=$!
echo "Parent: child (PID= $pid) launched."
echo "Parent: continuing..."
sleep 2
echo "Parent: pausing to wait for child to finish..."
wait $pid
echo "Parent: child is finished. Continuing..."
echo "Parent: parent is done. Exiting."

子脚本

#!/bin/bash
# async-child : Asynchronous execution demo (child)
echo "Child: child is running..."
sleep 5
echo "Child: child is done. Exiting."
  1. The process ID of the child script is recorded by assigning the pid variable with the value of the $! shell parameter, which will always contain the process ID of the last job put into the background. $!记录最后一个放入后台运行的进程ID
  2. 父脚本中调用子脚本的时候要指定位置

命名管道

Named pipes are used to create a connection between two processes and can be used just like other types of files.

There is a common programming architecture called client-server, which can make use of a communication method such as named pipes, as well as other kinds of interprocess communication such as network connections.

Named pipes behave like files, but actually form first-in first-out (FIFO) buffers. As with ordinary (unnamed) pipes, data goes in one end and emerges out the other. With named pipes, it is possible to set up something like this: 先入先出缓冲

process1 > named_pipe
process2 < named_pipe
 it will behave as if: process1 | process2
root@ubuntu:/code# mkfifo pipe1
root@ubuntu:/code# ls -l pipe1 
prw-r--r-- 1 root root 0 Feb 23 09:49 pipe1

  1. the first letter in the attributes field is “p”, indicating that it is a named pipe
[me@linuxbox ~]$ ls -l > pipe1

After we press the Enter key, the command will appear to hang. This is because there is nothing receiving data from the other end of the pipe yet. When this occurs, it is said that the pipe is blocked. This condition will clear once we attach a process to the other end and it begins to read input from the pipe.

[me@linuxbox ~]$ cat < pipe1

and the directory listing produced from the first terminal window appears in the second terminal as the output from the cat command. The ls command in the first terminal successfully completes once it is no longer blocked.

  1. 管道另一端没有对象接收数据时命令会被挂起,这被称为阻塞。
  2. 管道另一端一旦绑定了一个进程就会读取输入
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值