eval
eval command-line
,shell 在执行命令时会扫描两次。
1 | eval echo hello # 看上去没什么区别 |
shell 先进行管道操作和重定向再做变量替换,所以这里会报错。
eval
命令经常用于在一个或多个变量内构建命令行的 shell 程序中。如果变量包含必须由 shell 解释的任何字符,则 eval
是必不可少的。命令终止符(;
、|
、&
)、I/O 重定向(<
、>
)和引号字符是必须直接出现在命令行上才能对 shell 具有特殊含义的字符。
1 | 获取命令行最后一个参数,之前是用 `shift` 实现的 |
1 | 假设 arg 是个数字,想打印数字对应的位置参数 |
可以使用 eval 达到指针的效果
1 | x=100 |
wait
如果将命令移到后台执行,则该命令行将在独立于当前 shell 的子 shell 中运行(该作业称为异步运行)。然而,在某些情况下,可能需要等待后台进程(也称为子进程,因为它是从当前 shell(父进程)生成的)完成再继续执行。
1 |
|
$!
如果只有一个进程在后台运行,那么不带任何参数等待就足够了。但是,如果正在运行多个后台命令,并且想要等待最近启动的命令,则可以将最新后台命令的进程 ID 作为特殊变量 $!
进行访问。
wait $!
等待最后一个放到后台执行的程序完成。可以使用中间变量保存多个进程 id。
trap
在 Shell 编程中,trap
是一个用于捕捉和处理信号的命令。信号是操作系统与进程之间的一种通信方式,当某些事件(如用户中断、脚本退出、或特定的系统事件)发生时,操作系统会向正在运行的进程发送信号。通过 trap
命令,可以在特定信号到来时,执行指定的命令或脚本,而不是让默认行为发生。这样可以用来做清理工作、保存数据、或者执行其他必要的操作。
trap commands signals
其中 commands 是一个或多个命令,每当接收到 signals 指定的任何信号时将执行这些命令。
trap "rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" INT
,收到 INT
也就是 Ctrl+c
时的行为,如果这里没有 exit
,那么程序会继续执行。
如果指定的 trap sequence
也称为陷阱处理程序 trap handler)包含多个命令,则必须将其括在引号中。另外,shell 在执行 trap
命令时扫描命令行,并在收到列出的信号之一时再次扫描命令行。
在前面的示例中,WORKDIR
和 $$
的值将在执行 trap
命令时被替换。如果希望在收到信号时进行此替换,则可以将命令放在单引号内。
没有参数的 trap
直接调用 trap 会列出用户定义的或者修改的 trap handler
。
1 | trap 'echo logged off at $(date) >>$HOME/logoffs' EXIT # 这里使用单引号就是防止在定义 trap 时 shell 执行 date |
忽略信号
1 | trap "" SIGINT |
如果 trap handler
为空,就相当于忽略了对于的信号,trap "" SIGINT
,trap
可以使用信号编号、缩略形式(INT)、完整名称(SIGINT)来注册信号。建议使用完整形式方便阅读。
如果忽略某个信号,所有子 shell 也会忽略该信号。但是,如果指定信号处理程序操作,则所有子 shell 在收到该信号时都会自动采取默认操作,而不是新的代码序列(trap handlers)。
1 | !/bin/bash |
恢复 traps
trap HUP INT
如果忽略 handlers,就意味着使用系统默认的处理程序。
I/O
在 Shell 编程中,>&
是一种用于重定向输出和错误的语法,通常与文件描述符(file descriptor)结合使用。符号 >&
指定输出重定向到与后面的文件描述符关联的文件。
>&2
把标准输出重定向到标准错误输出,如果想把标准输出和标准错误都重定向到文件可以使用 command > foo 2>> foo
或者 command > foo 2>&1
,第二个是说把标准输出定向到文件,再把标准错误输出重定向到标准输出也就是标准输出。
shell 从左到右进行解析,所以不能写成 command 2>&1 > foo
。
如果想重定向当前 shell 的 I/O 怎么办?使用 exec
,exec < datafile
,exec > /tmp/output
,exec 2> /tmp/errors
,会影响后续所有命令的 I/O 行为。
<&- and >&-
用来关闭标准输入和输出,如果前面跟一个文件描述符相关的文件就会被关闭。
ls >&-
,没有任何输出,不是很常用。
In-line input Redirection
>>
和 <<
是不同的,前者是追加重定向;后者是 here document,将多行文本作为输入传递给命令。
1 | EOF 可以换成其他东西,首尾要一样 |
In-line input redirection—also referred to as here documents by some programmers—is a powerful feature when used inside shell programs。
here doc 中的特殊字符 $
、\
、反引号会被 shell 处理,如果不想让 shell 处理这些文件,可以在 end-of-document word
前加一个反斜线。
1 | cat <<\FOOBAR |
选择 <<
后面的单词(end-of-document word
)时要小心。一般来说,只要确保它足够奇怪,这样它意外出现在后续数据行中的可能性就很小了。
<<-
会移除 here doc 中的所有的 tab
,这样的好处是可以在 shell 中很好的格式化数据,但是输出时却不包含这些tab
。
1 | cat <<-END |
Shell Archives
使用 in-line input redirection
,可以创建 shell archive
文件。
通过这种技术,可以将一个或多个相关的 shell 程序放入一个文件中,然后使用标准 Unix 邮件命令发送给其他人。收到存档后,可以通过将其作为 shell 程序调用来”解包”。
1 | shell archives |
可以把这个文件发送给别人,相当于程序分发了。
1 |
|
Functions
函数是在当前 shell 执行的。
所有现代 shell
都支持定义函数,name () { command; command; }
,如果是单行定义,则第一个花括号有要有空格,最后一个命令要有分号。
函数只能在当前 shell 中使用,不能传递给 subshell。函数仅存在于定义它们的 shell 中,因此不能将它们传递给子 shell。由于该函数是在当前 shell 中执行的,因此对当前目录或变量所做的更改在该函数完成执行后仍然保留,就像使用 .
一样。
函数可以多行定义。
Removing a function definition
unset -f add
return
在函数中使用 exit
,不仅会退出函数还会退出当前 shell,使用 return n
退出函数,n 表示函数退出状态,如果没有写,那么使用最后一个命令的返回值。
type
执行的是 functin、shell built-in function、a standard Unix Command or alias 有时非常重要,可以使用 type
查看。