"bash 环境配置文件 一般来说ubuntu系统上有以下配置文件: /etc/profile :login shell的全局配置文件 /etc/bash.bashrc :non-login shell的全局配置文件 ~/.profile :login shell 的用户配置文件 ~/.bashrc ...."

linux 基础知识

bash 环境配置文件

一般来说ubuntu系统上有以下配置文件:

  • /etc/profile :login shell的全局配置文件
  • /etc/bash.bashrc :non-login shell的全局配置文件
  • ~/.profile :login shell 的用户配置文件
  • ~/.bashrc :non-login shell 的用户配置文件

关于login shell 和non-login shell的区别就不在这里展开了。有兴趣的可以自己搜索一下。我们只需要明白在我们登录服务器时,以上这些配置文件都会被加载一次。一般情况下我们只需要修改用户配置文件 .bashrc 就可以了。主要是设置 PATH 环境变量。

环境变量

环境变量其实跟 java 中的变量是同一个概念。

  • 全局环境变量:可以传递给子进程,类似于 java 类中的 public, protected 变量,可以被子类访问。
  • 局部环境变量:只在当前进程有效,类似于 java 类中的 private 变量,子类不能访问父类的 private 变量。

变量赋值方式为: foobar=value,需要注意的是 = 号两边不能有空格!!!
默认是局部变量,如果需要设置为全局变量,则使用: export foobar=value。或者

foobar=value
export foobar

环境变量中有一个非常重要的变量:PATH。PATH 变量保存了一系列目录的绝对路径,这些路径由 :号分隔。当我们输入一个命令的时候,shell 就会在 PATH 变量的这些目录中从头到尾的找这个命令,如果找不到就会报错。系统默认的 PATH 值为

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

系统内置的命令基本上都包含在这些目录里面了。如果我们想查看某个命令来自哪里的话,可以使用 which 命令

ubuntu@VM-171-157-ubuntu:~$ which java
/usr/bin/java
ubuntu@VM-171-157-ubuntu:~$

假如系统有两个版本的 jdk:jdk1.6,jdk1.7。默认的是 jdk1.6,编译 android 4.4 的时候需要用jdk1.6,编译 android5.1 的时候需要用jdk1.7。那我们怎么去切换不同版本的 jdk 呢?通过设置 PATH 的值,把想要的 jdk 版本路径放到最前面即可。

export PATH=/usr/lib/jvm/java-7-openjdk-amd64/bin:$PATH

我们91/98平台是在编译脚本 auto_make.sh 中设置的
ae4290686f0b4ed08fb011cb41df0ccb_blob.png

828平台则是在 $ANDROID_ROOT/build/envsetup.sh 文件中的 set_java_home 函数中设置的
0185174f0aea49e88121504ccdf7f6db_blob.png

文件权限

linux 文件权限系统是 linux 安全的基础。android 文件系统的隔离也是基于 linux 的文件系统权限完成的。android 系统每安装一个应用,系统就会为这个应用新增一个用户。在 /data/data 目录下为这个应用创建一个数据目录, 每个应用的数据目录只能本应该读写,其他应用没有权限读写。我们先来了解一下文件权限位。再去分析 android 的各应用数据文件的安全隔离。
使用 ls -l 命令可以查看文件的权限位:

xushaoqiu@ubuntu:~$ ls -l
total 44
drwxr-xr-x  3 xushaoqiu xushaoqiu 4096 Jul  7  2016 configure
-rwxr-x--x  1 xushaoqiu xushaoqiu  817 Sep 15  2015 createImage.sh
-rwxrwxr--  1 xushaoqiu xushaoqiu 1683 May 23  2016 format.c
drwxrwxr-x  7 xushaoqiu xushaoqiu 4096 Feb  6 17:21 local

第一列显示的即为权限位 drwxr-xr-x。说明如下:

d rwx r-x r-x
目录 user 可读/可写/可运行 group 可读/不可写/可运行 other 可读/不可写/可运行

第一个字母 d 表示这个文件是一个目录(linux下一切皆文件),值可能为:

  • -表示文件
  • d表示目录
  • l表示链接
  • c表示字符设备
  • b表示块设备
  • n表示网络设备

接下来的9个字符分成3组,每组3个字符,每个字符定义了一种权限

  • r 读权限
  • w 写权限
  • x 执行权限

第一组表示的是文件属主的权限。
第二组表示的是用户组成员的权限。
第三组表示的是其他用户的权限。

290d74f9dde445129a87dc20ccc1ae2a_blob.png

说了这么多,但我们平时修改文件权限的时候都是这样的

chmod 755 auto_make.sh

755 跟前面所说的 rwx r-x r-x 又是怎么对应的呢?
其实 linux 在底层是使用 12 个二进制位来表示文件权限的:

11  10  9  8  7  6  5  4  3  2  1  0

S    G   T  r  w  x  r  w  x  r  w  x

前三位叫粘着位,比较特殊,这里不讨论,我们只需要看后 9 位就行。
将它转换成二进制就看得明白了。用r,w,x这在个字母代表 1, -代表 0,代入得
111 101 101,三位一组转换成十进制就是 755。

windows没有文件权限位的概念,平台代码最好不要在windows下提交,可能会丢失可执行文件的执行权限

umask

前面我们已经基本了解了权限位的概念,那 linux 下创建文件或目录时默认的权限是什么?这就是由 umask 来决定的。查看当前系统的 umask :

xushaoqiu@ubuntu:~$ umask
0022
xushaoqiu@ubuntu:~$

umask 也叫权限掩码,它会在我们创建文件时拿走权限位,其实就是一个减法操作。
一个文件的最大权限为 777 ,当 umask 为 022 的时候,新建文件的权限 = (777 - 022)=755

xushaoqiu@ubuntu:~/tutor$ umask
0022
xushaoqiu@ubuntu:~/tutor$ mkdir dir
xushaoqiu@ubuntu:~/tutor$ touch file
xushaoqiu@ubuntu:~/tutor$ ll
drwxr-xr-x  2 xushaoqiu xushaoqiu 4096 Mar 21 15:29 dir/
-rw-r--r--  1 xushaoqiu xushaoqiu    0 Mar 21 15:29 file
xushaoqiu@ubuntu:~/tutor$

我们看到创建目录的时候,默认权限位是和我们预期的一样的了,但创建文件时好像结果有点出入。这是由于 linux 系统的安全特性,创建文件的时候会把执行权限位给拿掉。也就是:

  • 新创建目录的权限 = (777 - umask)
  • 新创建文件的权限 = (666 - umask)

之前在编译 hisi 平台代码的时候就遇到了一个由于 umask 设置不正确而导致的系统启动失败问题。错误 log 如下:

[    4.730604@1] init: skipping insecure file '/default.prop'
[    4.734521@1] init: skipping insecure file '/init.rc'
[    9.228365@1] init: Timed out waiting for /dev/.coldboot_done
[    9.239168@0] osd0=>x:0 ,y:0,w:1280,h:720
[    9.239173@0]  osd1=> x:0,y:0,w:18,h:18 
[    9.255924@0] init: Unable to open persistent property directory /data/property

由日志我们可以知道是 init 进程启动失败了,init在启动时会解析执行 /init.rc 文件 ,从 log 中发现 init 跳过了 init.rc 文件的解析,从而导致了后面的启动失败。为什么会跳过 init.rc 文件的解析呢?我们去看看init的源码就知道了,init的代码路径为 android/system/core/init。我们进入到这个目录,搜索一下在哪里打印了这个错误信息:

xushaoqiu@ubuntu:~/svn/android4.4_ultra/system/core/init$ grep -nr "skipping insecure file" *
util.c:161:        ERROR("skipping insecure file '%s'\n", fn);
xushaoqiu@ubuntu:~/svn/android4.4_ultra/system/core/init$

发现是在 util.c 文件中打印的,打开这个文件,定位到相应行看看:
8081a7f5de5246cca8a2c717dcb8f9f8_blob.png

S_IWGRP 即组用户可写,S_IWOTH 其他用户可写,可以这样理解:GRP 组,OTH 其他, W 写权限。这段代码决断要读取的文件的权限位,如果组可写或者其他用户可写,则出错。这里即是 init.rc 不能给组用户和其他用户写的权限。据说这是为了安全,但怎么个安全法,我就不懂了。因为就算你修改了 init.rc 文件,系统重启后,也会恢复的,因为这个文件每次启动都会从 ramdisk.img 镜像中加载的。除非重启生成 ramdisk.img 镜像。

到此就可以确定是文件权限不正确引起的问题了。查看系统的 umask 值为 0002,将其修改为 0022

umask 0022

然后重新 svn checkout 代码,重新编译后就能正常启动了。

输入输出重定向

重定向的内容比较简单,一般来说我们只需要理解下面这三个重定向符即可。

>    输出重定向到一个文件或设备 覆盖原来的文件
>>  输出重定向到一个文件或设备 追加原来的文件
<    输入重定向到一个程序

我们在以下情景的时候可能需要用到重定向操作。

  • 当屏幕输出的信息很重要,而且我们需要将他存下来的时候;(编译脚本)
  • 后台执行中的程序,不希望他干扰屏幕正常的输出结果时;(logcat)
  • 一些系统的例行命令(例如写在 /etc/crontab 中的文件)的执行结果,希望他可以存下来时;(daily build)

管道 |

管道是一种两个进程间进行单向通信的机制。我们可以理解为数据队列,进程 A 不断的往队列中写数据,进程 B 不断的从队列中读数据,典型的生产者-消费者模型。一般用来级联处理数据。
比如说我们需要获取 svn 中被修改的文件,并提交,则可以这么干:

svn st | grep ^M | awk '{print $2}' | xargs svn commit -m 'commit message'

程序中断 ctrl-cctrl-dctrl-z 傻傻分不清楚

这个是由于 windows 与 linux 的一些差异引起的误解。

  • ctrl-c :windows 和 linux 都表示中断程序
  • ctrl-d :linux 下表示表示输入结束 EOF
  • ctrl-z :windows 下表示输入结束,linux 下表示将程序转到后台运行

习惯了windows操作的同学,可能会误用 ctrl-z 当作中断程序。时间长了可能就会导致一堆的后台程序在跑,导致系统卡顿。

如果有种情况出现的话,可以使用 fg 命令把后台进程给切换到前台,然后 ctrl-c 中断即可。
还有一种情况就是出现僵尸进程了,而且这个僵尸进程很耗系统资源,fg 也调不到前台,这个时候只能 top命令看一些有哪些异常的进程。然后用kill -9 pid 强制杀掉这些僵尸进程。

实用工具

tmux

tmux 我认为是最重要的工具了,所以把它放在了第一位。而 tmux 最重要的功能则是会话保持。就是我开启了一个 tmux 会话之后,就算我断开服务器的链接了,该会话还会在继续运行。这个特性对于我们经常需要挂机编译代码的同学非常有用。具体用法就不展开了,更多技巧请 google。

grep

grep 的功能主要是文本匹配过滤。下面分享一些在工作过程中经常会用到的小技巧。

  • 全文检索
    我们经常要在 android 代码的汪洋大海中找某些关键字出现在哪里。这个时候 grep 就派上用场了,只要执行下面的命令即可:

    grep -nr "foobar"
    

    参数 n表示输出匹配第几行,r表示递归查找当前目录下的所有文件。

  • 过滤多个模式

    #找出文件(filename)中包含123或者包含abc的行
    grep -E '123|abc' filename
    

    需要加上 -E 选项用以支持扩展正则表达式,因为标准的正则表达式不支持 | 语法。

  • 反向匹配
    有时候我们在查看 logcat 的时候有很多烦人的打印不断地在刷屏。这些信息我们确定是不需要的,我们可以用 grep 的反向匹配功能把这些垃圾信息给过滤掉:

    logcat | grep -v "rubbish"
    
  • 输出匹配行的前后N行

    grep -5 'parttern' inputfile #打印匹配行的前后5行
    grep -C 5 'parttern' inputfile #打印匹配行的前后5行
    grep -A 5 'parttern' inputfile #打印匹配行的后5行
    grep -B 5 'parttern' inputfile #打印匹配行的前5行
    

更多技巧请 google。

sed

sed 一般用来自动编辑文本文件。在我们的自动编译脚本中,配置信息的读写功能基本上全是用 sed 来完成的。sed 可以说是这些命令行工具中最复杂的了,下面来展示一些基本的用法。sed 的使用基本上伴随着正则表达式,所以我们需要对正则表达式有个基本的概念。关于正则表达式可以专门用一章来说明了,请自行google。

  • 去掉行首和行尾的空白符:

    sed 's/^[[:space:]]*\(.*[^[:space:]]\)\([[:space:]]*\)$/\1/g'
    

    此正则表达式拆解:

    ^[[:space:]]* : 行首的空白符
    \(.*[^[:space:]]\) : 任意字符串,且不以空白符结尾
    \([[:space:]]*\)$ : 行末空白符
    
  • 配置信息读取

  • 配置信息修改

更多技巧请 google。

awk

awk 一般用来提取有比较规范输出格式部分字段。比如说 svn st 的 log。


第一段是文件的状态 ?/A/M/I/C等,第二段是文件的路径。
我们想要提取被修改过的文件路径字段的话可以这么干:

svn st | grep ^M | awk '{print $2}'

awk 的默认各字段分隔符是空白符(空格和 TAB),我们也可以指定分隔符:

dep@dep-pc:~$ echo "key=value" | awk -F"=" '{print $2}'
value
dep@dep-pc:~$ echo "key,value" | awk -F"," '{print $2}'
value
dep@dep-pc:~$

对于 awk ,我好像也就用过这个功能了,已经能够满足我的需求了。更多的用法请 google。

sort 排序

uniq 去重

wc 字符统计

tr 大小写转换

0     1     0     0     0    
0 回帖