Overview
1. sh -c
的必要性
本节 转自 ? https://blog.csdn.net/bobchill/article/details/84647575
在Linux使用 echo 并配合命令重定向是实现向文件中写入信息的快捷方式。
比如要向 test.asc 文件中随便写入点内容,可以:
$ echo "信息" > test.asc
# 或者
$ echo "信息" >> test.asc
下面,如果将 test.asc 权限设置为只有 root 用户才有权限进行写操作:
$ sudo chown root.root test.asc
然后,我们使用 sudo 并配合 echo 命令再次向修改权限之后的 test.asc 文件中写入信息:
$ sudo echo "又一行信息" >> test.asc
命令执行结果:
-bash: test.asc: Permission denied
这时,可以看到 bash 拒绝这么做,说是权限不够。这是因为重定向符号 “>” 和 “>>” 也是 bash 的命令。
我们使用 sudo 只是让 echo 命令具有了 root 权限,但是没有让 “>” 和 “>>” 命令也具有 root 权限,所以 bash 会认为这两个命令都没有像 test.asc 文件写入信息的权限。
解决这一问题的途径有两种。第一种是利用 “sh -c” 命令,它可以让 bash 将一个字串作为完整的命令来执行,这样就可以将 sudo 的影响范围扩展到整条命令。具体用法如下:
$ sudo sh -c 'echo "又一行信息" >> test.asc'
另一种方法是利用管道和 tee 命令,该命令可以从标准输入中读入信息并将其写入标准输出或文件中,具体用法如下:
$ echo "第三条信息" | sudo tee -a test.asc
注意,tee 命令的 “-a” 选项的作用等同于 “>>” 命令,如果去除该选项,那么 tee 命令的作用就等同于 “>” 命令。
2. 程序的环境变量与“终端”的环境变量异同
2.1 终端环境变量
我们在终端中执行脚本/程序、命令工具(是脚本/程序)(下面统称程序),这些程序会被设置环境变量。
只针对终端而言的环境变量,可以通过 $HOME/.bashrc
来设置。这是通常教程,网络资源中最常见的方式。
但是 .bashrc
实际上是对 bash 程序而言的 - 是的,终端窗口上运行的本身也就是一个程序 - bash 程序。
所以,有的朋友为了提高效率、为了观感,而选择不同的 shell 软件 - 如 ZSH
。如果运行的是 ZSH,那么 .bashrc
中用户自定义设置的环境变量就没有用了。而是需要加入到 $HOME/.zshrc
文件中。
2.2 So, Global Environment Variables?
当我们去了解 LINUX 的环境变量的时候,/etc/profile
/ /etc/environment
也是经常被提到的。
但是在我这里过多不会介绍它们,因为修改这几个文件都是不建议的方式(当你真正有需要修改系统全局变量的时候,只要知道 /etc/profile
这个路径即可,怎么改?有什么影响?自然到时候就知道了)。
2.3 User-Global Environment Variables?
如果你是一个 coder,有时候需要使用第三方厂商提供的 API 接口 ——需要注册账号,申请 key;同时你又想要将编写的程序开源。
于是每次都要在代码中修改(删除) key 之后再 commit 代码。
No, no, no! 我们可以通过环境变量解决这个问题。在 Python 中,通过以下两行代码就可以取到环境变量值:
import os
VENDOR_KEY = os.environ.get("VENDOR_KEY")
即,我们只需要配置 VENDOR_KEY
,然后按平常运行程序即可,此时程序就能取得环境变量,得到正确的 KEY 值。
如果该程序是在命令行中运行的,那么可以根据 2.1 节中修改 $HOME/.<shell-program>rc
文件即可。
但是有的程序是 desktop GUI,换句话说,我们想要修改一处地方,使这个环境变量对登录的用户(如’自己‘)执行的任意程序都有效。
那么我们应该修改 $HOME/.profile
这个文件。
...
export VENDOR_KEY=the-key-value
修改文件之后不会马上就生效。我们可以通过 <用户> ->“注销” -> 重新登录来使环境变量生效。
2.4 应用程序(desktop GUI)环境变量
其实在写 2 环境变量 的时候,我实在不确定应该将内部小节的顺序如何放置。
因为这就好像是先有鸡还是先有蛋的问题。不过内容不长,所以顺序上还是顺便就好,因为这些内容前后节都是直接关联的。
正如前面小节说到的,如果我们只修改如 $HOME/.bashrc
,那么它只会对 BASH 程序有效。而 BASH 程序就是终端窗口内运行的程序 - 用于执行命令行程序(环境变量如果没有在程序内修改的化,那么程序就会继承它父进程的环境变量),从 bash 内被执行的程序能够拥有和 bash 一样的环境变量;但是对于在 desktop 环境中双击执行的程序就不能拥有这些环境变量了。
LINUX 可以安装不同的 desktop 程序,如 KDE,Gnome 等等,所以我们也不应该去想只修改该桌面的环境变量。还是一步到位,直接修改上一节提到的 $HOME/.profile
对本用户的所有程序有效。包括终端程序。
3. “宏”(预定义符号)
“宏” | 含义 | “真实”值 |
---|---|---|
EOF | 数据流结束,无更多数据 | -1 |
. | . | . |
4. 文件描述符
4.1 Too many open filej
(C 语言)如果调用 fopen
打开某个文件 "r"
,得到 NULL
,未必是文件不存在。
如果直接在代码中写:
const char *file = "/<path>/<to>/some_file.txt";
FILE *fp = NULL;
if ((fp=open(file, "r")) == NULL) {
fprintf(stderr, "%s not exist.", file);
exit(1);
}
// do real read stuff
fclose(fp);
写出这种代码不算最糟糕的,但是也属于几乎不可用的代码。
因为如果一个程序中的其它地方有打开其它文件,但是 coder 忘记 fclose
,而这是一个"daemon" 进程的话。
那么如果上面位置 open
fail,此时实际错误是进程允许的最大文件描述符被用光。而非是文件不存在。
所以这么写代码反而有严重的误导性,而原本需要修改的地方也并非这里。
更好的写法:
const char *file = "/<path>/<to>/some_file.txt";
FILE *fp = NULL;
if ((fp=open(file, "r")) == NULL) {
perror(file);
exit(1);
}
// do real read stuff
fclose(fp) ? perror(file) : (fp = NULL);
// maybe more code under ...
5. 链接库
5.1 动态链接库 – .so
查看二进制程序或 .so 使用到的 .so:
使用 ldd 工具:
# ldd /usr/bin/iwinfo
/lib/ld-musl-arm.so.1 (0x54b71000)
libuci.so => /lib/libuci.so (0x76f9f000)
libubox.so => /lib/libubox.so (0x76f86000)
libubus.so => /lib/libubus.so (0x76f71000)
libiwinfo.so => /usr/lib/libiwinfo.so (0x76f57000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x76f3c000)
libc.so => /lib/ld-musl-arm.so.1 (0x54b71000)
#
# ldd /usr/lib/lua/iwinfo.so
ldd (0x54b36000)
libuci.so => /lib/libuci.so (0x76ef2000)
libubox.so => /lib/libubox.so (0x76ed9000)
libubus.so => /lib/libubus.so (0x76ec4000)
libiwinfo.so => /usr/lib/libiwinfo.so (0x76eaa000)
liblua.so.5.1.5 => /usr/lib/liblua.so.5.1.5 (0x76e7b000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x76e60000)
libc.so => ldd (0x54b36000)
#
查看 .so 文件接口:
$ nm -D xxx.so
...
打包成 .so:
n/a
5.2 静态链接库 – .a
查看 .a 文件都包含了哪些文件:
$ ar -t xxx.a
...
使用 ar
命令打包:
n/a