1、Link Map File 是啥
我們編寫的OC代碼需要經(jīng)過預(yù)編譯->編譯->匯編->鏈接(靜態(tài)鏈接)纬霞,最終生成一個(gè)可執(zhí)行文件冻辩。匯編階段完成后,每個(gè)類都會(huì)生成一個(gè)對(duì)應(yīng)的.o
文件(可重定位的目標(biāo)文件)触幼,在鏈接階段(靜態(tài))硼瓣,會(huì)把所有的.o
文件鏈接到一起,生成一個(gè)可執(zhí)行文件置谦。Link Map File就是這樣一個(gè)記錄鏈接相關(guān)信息的純文本文件堂鲤,里面記錄了可執(zhí)行文件的路徑、CPU架構(gòu)媒峡、目標(biāo)文件瘟栖、符號(hào)等信息。
2谅阿、探索 Link Map File 的意義
探索 Link Map File 可以幫助我們更好的理解鏈接過程半哟、理解內(nèi)存分段及分區(qū)酬滤、分析可執(zhí)行文件中哪個(gè)類或庫占用比較大,進(jìn)行安裝包瘦身寓涨。
蘋果對(duì)上傳到App Store上的app大小也有嚴(yán)格的規(guī)定 Apple 規(guī)定最大構(gòu)建版本文件大小
翻譯:
執(zhí)行文件大小是指執(zhí)行文件的__TEXT部分
- 當(dāng)iOS最低版本小于7.0盯串,最多為80MB;
- 想iOS系統(tǒng)版本位于 7.0~8.0之間時(shí)戒良,每個(gè)分區(qū)是60MB(并不是指32位+64位最多為120MB体捏,當(dāng)32位分區(qū)占用50MB,64位分區(qū)占用61MB蔬墩,總111MB也不行译打,因?yàn)?4位分區(qū)超出了);
- 當(dāng)iOS大于等于9.0拇颅,總限制500MB奏司;
對(duì)App來說,可以用Link Map File 來分析各個(gè)文件占用大小樟插,有針對(duì)性進(jìn)行優(yōu)化韵洋。
對(duì)SDK來說,也可以用Link Map File 來計(jì)算自身大小黄锤,控制好大小這個(gè)度搪缨。一般大App對(duì)集成二方和三方這樣的SDK大小是有很嚴(yán)格的限制。
3鸵熟、如何生成 Link Map File
Xcode 默認(rèn)情況下是不會(huì)去生成 Link Map File副编,需要開發(fā)者自己手動(dòng)去打開生成配置開關(guān),如下圖流强,Target -> Build Setting -> Linking -> Write Link Map File 為 YES
存放 Link Map File 文件路徑如下圖
默認(rèn)存放路徑是
$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt
最終翻譯成實(shí)體文件路徑如下
/Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Demo-LinkMap-normal-x86_64.txt
我們也可更改 Link Map File 文件存放位置痹届,這個(gè)要看自己需要了
4、Link Map File 組成
1. Path:生成可執(zhí)行文件的路徑
# Path: /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Products/Debug-iphonesimulator/Demo.app/Demo
2. Arch:架構(gòu)類型
# Arch: x86_64
3. Object files:列舉了可執(zhí)行文件里所有的obj以及tbd打月。
# Object files:
[ 0] linker synthesized
[ 1] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Demo.app-Simulated.xcent
[ 2] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Objects-normal/x86_64/ViewController.o
[ 3] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Objects-normal/x86_64/AFSecurityPolicy.o
[ 4] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Objects-normal/x86_64/AppDelegate.o
...
4. Sections:主要用展示代碼和數(shù)據(jù)在內(nèi)存中的分布队腐,涉及到Mach-O可執(zhí)行文件內(nèi)存分布相關(guān),這里不做過多解釋
# Sections:
# Address Size Segment Section
0x100004350 0x0002308D __TEXT __text
0x1000273DE 0x000002A0 __TEXT __stubs
0x100027680 0x00000470 __TEXT __stub_helper
......
0x100030000 0x00000008 __DATA __nl_symbol_ptr
0x100030008 0x000000F0 __DATA __got
0x1000300F8 0x00000380 __DATA __la_symbol_ptr
......
5. Symbols:記錄符號(hào)相關(guān)信息奏篙,也是我們統(tǒng)計(jì)的關(guān)鍵
# Symbols:
# Address Size File Name
0x100004350 0x00000040 [ 2] -[ViewController viewDidLoad]
0x100004390 0x000000E0 [ 2] -[ViewController touchesBegan:withEvent:]
0x100004470 0x00000050 [ 2] ___41-[ViewController touchesBegan:withEvent:]_block_invoke
0x1000044C0 0x00000080 [ 2] ___41-[ViewController touchesBegan:withEvent:]_block_invoke_2
0x100004540 0x00000074 [ 2] ___41-[ViewController touchesBegan:withEvent:]_block_invoke_3
0x1000045C0 0x00000330 [ 3] +[AFSecurityPolicy certificatesInBundle:]
0x1000048F0 0x00000080 [ 3] +[AFSecurityPolicy defaultPolicy]
根據(jù)Sections
的起始地址柴淘,可以將Symbols
分為Sections
個(gè)數(shù)的組
Symbols包含的信息有:
-
Address
:符號(hào)起始地址 -
Size
:所占內(nèi)存大小(16進(jìn)制)秘通。 -
File
:該Name
所在的文件編號(hào)为严,也就是Object files
部分的中括號(hào)的數(shù)字,例如-[ViewController viewDidLoad]
對(duì)應(yīng)的文件編號(hào)為[ 2]
肺稀,根據(jù)Object files
部分可以看到所屬的文件為:ViewController.o
梗脾。這樣可以計(jì)算某個(gè).o
文件所占內(nèi)存的大小。只需要把Symbols
中編號(hào)為.o
對(duì)應(yīng)編號(hào)符號(hào)累加統(tǒng)計(jì)即可盹靴。 -
Name
:符號(hào)的名稱炸茧。
6. Dead Stripped Symbols:鏈接器認(rèn)為無用的符號(hào)瑞妇,統(tǒng)計(jì)大小的時(shí)候不統(tǒng)計(jì)它下面的符號(hào)
# Dead Stripped Symbols:
# Size File Name
<<dead>> 0x00000018 [ 2] CIE
<<dead>> 0x00000015 [ 3] literal string: supportsSecureCoding
<<dead>> 0x0000000F [ 3] literal string: SSLPinningMode
<<dead>> 0x00000011 [ 3] literal string: pinnedPublicKeys
5、Link Map File 信息統(tǒng)計(jì)
接下里正式進(jìn)入本文主題梭冠,使用 Link Map 文件統(tǒng)計(jì)各個(gè)文件的大小辕狰。
說到統(tǒng)計(jì),真的是心酸的不行控漠,開始寫了一個(gè)shell的統(tǒng)計(jì)腳本蔓倍,結(jié)果發(fā)現(xiàn)計(jì)算速度太慢(主要是沒有面向?qū)ο蟮乃枷耄矝]有我想要的數(shù)據(jù)結(jié)構(gòu)盐捷,例如map)右冻。為了加快統(tǒng)計(jì)的速度险绘,我用OC寫了個(gè)linkmap計(jì)算程序,速度是快了,但是想到運(yùn)行OC畢竟要裝Xcode才能運(yùn)行蹂随,哎误续!還能怎樣这刷,接著探索唄百匆。這個(gè)時(shí)候我想到一門語言,Python习霹! 面向?qū)ο髮懩_本朵耕,正是我需要的,于是扒資料扒教程得去學(xué)習(xí)Python淋叶,學(xué)完出山阎曹,寫了一個(gè)linkmap.sh程序,計(jì)算速度果然大幅度增加煞檩,比較滿意处嫌。這樣就滿足了嗎?不形娇!生命不息,折騰不止筹误!這讓我想起了之前無意間看到C++之父說過的一句話桐早,大概的意思是“我為全球變暖做出的貢獻(xiàn)就是讓C ++運(yùn)行更高效”,這句話激勵(lì)了我去重新去認(rèn)識(shí)C ++這門語言厨剪,于是我買了李明杰的C++課程哄酝,一番學(xué)習(xí)下來收獲頗豐,于是用C ++重寫了計(jì)算程序祷膳。
5.1 人狠話不多陶衅,先上耗時(shí)對(duì)比
首先我用工程編譯出一個(gè) Link Map File,有 1.7w 多行符號(hào)直晨,也就是說腳本要解析計(jì)算1.7w多行文本信息
程序 | 耗時(shí) |
---|---|
shell腳本 | 548.183s |
Python腳本 | 0.231s |
C++程序 | 0.107s |
5.2 慢速的shell
初期寫了個(gè)shell統(tǒng)計(jì)腳本搀军,但是shell腳本沒有map這樣的數(shù)據(jù)結(jié)構(gòu)膨俐,這讓人很痛苦,因?yàn)橐粋€(gè)文件可能對(duì)應(yīng)很多符號(hào)罩句,解析到某一個(gè)符號(hào)之后焚刺,首先要找出文件,再找出文件之前統(tǒng)計(jì)的大小门烂,加上該符號(hào)大小乳愉,以此類推。設(shè)計(jì)上我用了兩個(gè)數(shù)組屯远,一個(gè)裝文件編號(hào)蔓姚,一個(gè)裝文件對(duì)應(yīng)累加大小,兩個(gè)數(shù)組通過位置對(duì)應(yīng)起來慨丐。解析某個(gè)文件對(duì)應(yīng)的符號(hào)需要遍歷兩個(gè)數(shù)組坡脐,所以慢的要死。腳本代碼如下
#!/bin/sh
if [[ $# < 1 ]]; then
echo "腳本正確使用方式:./linkmap.sh <link-map-file-pat> <keyword>"
echo "示例:./linkmap.sh ./linkmap.txt"
echo "示例:./linkmap.sh ./linkmap.txt ATAuthSDK"
exit 0
fi
declare -a file_number_arr
declare -a file_name_arr
declare -a file_size_arr
reach_files=0
reach_sections=0
reach_symbols=0
number=`cat $1 | wc -l`
progress_view=''
current_number=0
progress=0
while read line
do
#進(jìn)度條相關(guān)
((current_number++))
if [[ $((current_number * 100 / number)) -ge $((progress + 1)) ]]; then
progress_view+='#'
progress=$((current_number * 100 / number))
fi
printf "[%-100s] %d%% \r" "$progress_view" "$progress";
if [[ -n `echo $line | grep '^# Object files:'` ]]; then
reach_files=1
elif [[ -n `echo $line | grep '^# Sections:'` ]]; then
reach_sections=1
elif [[ -n `echo $line | grep '^# Symbols:'` ]]; then
reach_symbols=1
elif [[ -n `echo $line | grep '^# Dead Stripped Symbols:'` ]]; then
break;
fi
if [[ $reach_files -ne 0 && $reach_sections -eq 0 && $reach_symbols -eq 0 ]]; then
if [[ -n `echo $line | grep ']'` ]]; then
count=${#file_number_arr[@]}
file_number_arr[count]=`echo $line | egrep -o '\[([0-9 ]*)\]'`
file_name_arr[count]=${line#*] }
file_size_arr[count]=0
fi
elif [[ $reach_files -ne 0 && $reach_sections -ne 0 && $reach_symbols -ne 0 ]]; then
file_number=`echo $line | egrep -o '\[([0-9 ]*)\]'`
file_size=`echo ${line%]*} | cut -d ' ' -f 2`
echo "第 $current_number 行 大小為:$file_size" >> log.txt
if [[ -n "${file_number}" && -n "${file_size}" ]]; then
idx=-1
for (( i = 0; i < ${#file_number_arr[@]}; i++ )); do
if [[ "${file_number_arr[$i]}" = "${file_number}" ]]; then
idx=$i
fi
done
if [[ $idx -ge 0 ]]; then
file_size_arr[$idx]=$(( ${file_size_arr[$idx]} + ((file_size)) ))
fi
fi
fi
((tag++))
done < "${1}"
total_size=0
printf "\n%-40s \t\t %-10s %-20s\n" "文件名" "文件編號(hào)" "文件大小"
for (( i = 0; i < ${#file_name_arr[@]}; i++ )); do
if [[ -n "${2}" ]]; then
if [[ -n `echo "${file_name_arr[i]}" | grep "${2}"` ]]; then
printf "%-40s \t %-10s %-20s\n" "${file_name_arr[$i]##*/}" "${file_number_arr[$i]}" "${file_size_arr[$i]} byte"
((total_size+=${file_size_arr[$i]}))
fi
else
printf "%-40s \t %-10s %-20s\n" "${file_name_arr[$i]##*/}" "${file_number_arr[$i]}" "${file_size_arr[$i]} byte"
((total_size+=${file_size_arr[$i]}))
fi
done
echo "\n總大小 ${total_size} byte ≈ $((total_size / 1024)) kb"
5.3 高鐵速度的Python
對(duì)比上面的shell腳本咖气,Python腳本快了不止一倍兩倍挨措,上面有數(shù)據(jù)統(tǒng)計(jì)。因?yàn)镻ython是面向?qū)ο蟮木幊陶Z言崩溪,所以代碼上更清晰明朗浅役,看起來也很舒服,同時(shí)還增加了排序功能伶唯,可以根據(jù)需要觉既,選擇文件名排序、文件大小升序排序乳幸、文件大小降序排序中的一種瞪讼,只需要傳入不同的參數(shù)即可實(shí)現(xiàn)。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys,os
from enum import Enum, unique
@unique
class LMPStep(Enum):
Initial = 0
Object = 1
Section = 2
Symbols = 3
Finish = 4
class LMPOrder(Enum):
FileName = 0 #按文件名輸出顯示
SizeAsc = 1 #按文件大小升序排序
SizeDesc = 2 #按文件大小降序排序
class LMPModel(object):
"符號(hào)信息記錄類"
def __init__(self, number, path, name, size=0):
self.number = number
self.path = path
self.name = name
self.size = size
def add(self, size):
self.size += size
def __str__(self):
return 'number=%s,name=%s,size=%d' \
% (self.number, self.name, self.size)
def build_symbol_model(line):
"根據(jù) # Object files: 中的行構(gòu)建 LCSymbolModel 對(duì)象"
if '[' not in line or ']' not in line:
return None
snumber = line[line.index('['):line.index(']') + 1]
spath = line[line.index(']') + 1:]
sname = spath.split('/')[-1].strip()
model = LMPModel(snumber, spath, sname)
return model
def parse_linkmap_file(filepath):
if os.path.exists(filepath) == False:
print '請(qǐng)檢查 %s 文件是否存在' % filepath
return None
try:
linkmap_file = open(filepath, mode='r')
except Exception as e:
print '%s 文件讀取失敗' % filepath
return None
dict = {}
step = LMPStep.Initial
line = linkmap_file.readline()
while line:
line = line.strip()
if len(line) > 0:
if line.startswith('#'):
if line.startswith('# Object files:'):
step = LMPStep.Object
elif line.startswith('# Sections:'):
step = LMPStep.Section
elif line.startswith('# Symbols:'):
step = LMPStep.Symbols
elif line.startswith('# Dead Stripped Symbols:'):
step = LMPStep.Finish
else:
if step == LMPStep.Object:
model = build_symbol_model(line)
if model and len(model.number) > 0:
dict[model.number] = model
else:
print 'Object 解析異常:%s' % line
elif step == LMPStep.Symbols:
snumber = line[line.index('['):line.index(']') + 1]
array = line.split('\t')
if len(snumber) <= 0 or snumber not in dict or len(array) < 3:
print 'Symbols 解析異常:%s' % line
else:
size = int(array[1], 16)
dict[snumber].add(size)
pass
elif step == LMPStep.Finish:
break
line = linkmap_file.readline()
linkmap_file.close()
return dict
def show(dict, keyword = None, order = LMPOrder.FileName):
total_size = 0
if dict:
if order == LMPOrder.SizeAsc.value:
models = sorted(dict.values(), key = lambda m: m.size)
elif order == LMPOrder.SizeDesc.value:
models = sorted(dict.values(), key = lambda m: m.size, reverse=True)
else:
models = sorted(dict.values(), key = lambda m: m.name)
for model in models:
if keyword:
if keyword in model.name:
print '%-40s %-6d byte' % (model.name, model.size)
total_size += model.size
else:
print '%-40s %-6d byte' % (model.name, model.size)
total_size += model.size
print '總大小 %d byte ≈ %.2f kb' % (total_size, total_size / 1024.0)
if __name__ == '__main__':
if len(sys.argv) < 2:
print '腳本正確使用方式:./linkmap.py <link-map-file-pat> <keyword> <order(0:文件名 1:文件大小升序 2:文件大小降序)>'
print '示例:./linkmap.py ./linkmap.txt'
print '示例:./linkmap.py ./linkmap.txt ATAuthSDK'
print '示例:./linkmap.py ./linkmap.txt ATAuthSDK 0'
sys.exit(0)
filepath = sys.argv[1]
keyword = None
order = LMPOrder.FileName
if len(sys.argv) > 2:
keyword = sys.argv[2]
if len(sys.argv) > 3:
order = int(sys.argv[3])
dict = parse_linkmap_file(filepath)
if dict:
show(dict, keyword, order)
5.4 火箭速度的C++
#include <string>
#include <unordered_map>
#include <fstream>
#include <vector>
#include <iomanip>
using namespace std;
class Model {
string number;
string path;
string name;
int size;
public:
Model();
Model(string number, string path);
void add(int size);
string getNumber();
string getPath();
string getName();
int getSize();
friend ostream& operator<<(ostream &, const Model&);
};
void parse_linkmap_file(string&, unordered_map<string, Model>&);
void show(unordered_map<string, Model>&, string&, int);
int main(int argc, const char * argv[]) {
//1. 參數(shù)檢查
if (argc < 2) {
cout << "命令正確使用方式:./linkmap <link-map-file-path> <keyword> <order(0:文件名 1:文件大小升序 2:文件大小降序)>" << endl;
cout << "示例:./linkmap ./linkmap.txt" << endl;
cout << "示例:./linkmap ./linkmap.txt ATAuthSDK" << endl;
cout << "示例:./linkmap ./linkmap.txt ATAuthSDK 0" << endl;
return 0;
}
string filepath = argv[1];
string keyword{};
if (argc > 2) {
keyword = argv[2];
}
int order = 0;
if (argc > 3) {
order = *(argv[3]) - '0';
}
//2. 解析
unordered_map<string, Model> map = {};
parse_linkmap_file(filepath, map);
//3. 展示
show(map, keyword, order);
return 0;
}
Model::Model() {}
Model::Model(string number, string path) : number(number), path(path), size(0) {
string::size_type idx = path.rfind('/');
if (idx == string::npos) {
name = path;
} else {
name = path.substr(idx + 1);
}
}
string Model::getNumber() { return number; }
string Model::getPath() { return path; }
string Model::getName() { return name; }
int Model::getSize() { return size; }
void Model::add(int size) {
this->size += size;
}
bool starts_with(const string& s1, const string& s2) {
return s1.size() >= s2.size() && s1.compare(0, s2.size(), s2) == 0;
}
vector<string> split(const string& str, const string& delim) {
vector<string> tokens;
size_t prev = 0, pos = 0;
do {
pos = str.find(delim, prev);
if (pos == string::npos) {
pos = str.size();
}
string token = str.substr(prev, pos - prev);
if (!token.empty()) {
tokens.push_back(token);
}
prev = pos + delim.size();
} while (pos < str.size() && prev < str.size());
return tokens;
}
string find_number(const string& line) {
string::size_type ns = line.find('[');
string::size_type ne = line.find(']');
if (ns == string::npos || ne == string::npos || ns > ne) {
return string();
}
return line.substr(ns, ne - ns + 1);
}
Model build_model(const string& line) {
string snumber = find_number(line);
if (snumber.size() == 0) {
return Model();
}
string spath = line.substr(line.find(']')+2);
return Model(snumber, spath);
}
void parse_linkmap_file(string& filepath, unordered_map<string, Model>& map) {
ifstream fin(filepath);
if (!fin) {
cout << "請(qǐng)檢查 " << filepath << " 是否存在" << endl;
return;
}
enum { Initial, Object, Section, Symbols, Finish } step;
step = Initial;
string line;
while (getline(fin, line)) {
if (line.size() == 0) continue;
if (starts_with(line, "#")) {
if (starts_with(line, "# Object files:")) {
step = Object;
}
else if (starts_with(line, "# Sections:")) {
step = Section;
}
else if (starts_with(line, "# Symbols:")) {
step = Symbols;
}
else if (starts_with(line, "# Dead Stripped Symbols:")) {
step = Finish;
}
}
else {
if (step == Object) {
Model m = build_model(line);
if (m.getNumber().size() > 0) {
map[m.getNumber()] = m;
} else {
cout << "Object 解析異常:" << line << endl;
}
}
else if (step == Symbols) {
string snumber = find_number(line);
vector<string> array = split(line, "\t");
if (snumber.size() == 0 || map.count(snumber) == 0 || array.size() < 3) {
cout << "Symbols 解析異常:" << line << endl;
}
else {
int size = (int)strtol(array[1].c_str(), nullptr, 16);
unordered_map<string, Model>::iterator it = map.find(snumber);
it->second.add(size);
}
}
else if (step == Finish) {
break;
}
}
}
fin.close();
}
void show(unordered_map<string, Model>& map, string& keyword, int order) {
typedef pair<string, Model> Pair;
int total_size = 0;
vector<Pair> pairs(map.begin(), map.end());
sort(pairs.begin(), pairs.end(), [=](Pair& lhs, Pair& rhs) {
if (order == 1) {
return lhs.second.getSize() < rhs.second.getSize();
}
else if (order == 2) {
return lhs.second.getSize() > rhs.second.getSize();
}
else {
return lhs.second.getName() < rhs.second.getName();
}
});
for (int i = 0; i != pairs.size(); ++i) {
Model model = pairs[i].second;
if (keyword.size() > 0) {
if (model.getName().find(keyword) != string::npos) {
cout << model << endl;
total_size += model.getSize();
}
}
else {
cout << model << endl;
total_size += model.getSize();
}
}
cout << "總大小 " << total_size << " byte ≈ " << total_size / 1024.0 << " kb" << endl;
}
ostream& operator<<(ostream& os, const Model& m) {
os << left << setw(8) << m.number << left << setw(40) << m.name << m.size;
return os;
}
C++ 代碼寫出來了粹断,接下來就是編譯成可執(zhí)行文件了
5.4.1 Mac OS 系統(tǒng)下
當(dāng)然是使用Xcode自帶的Clang
編譯器了符欠,雖然底層用的還是GCC
,編譯指令如下
clang++ -std=c++11 -stdlib=libc++ linkmap.cpp -o linkmap
-
-std
指定編譯的標(biāo)準(zhǔn)瓶埋,我這里選擇的是C++11這個(gè)標(biāo)準(zhǔn)希柿,因?yàn)槲业拇a是基于該標(biāo)準(zhǔn)開發(fā)的 -
-stdlib
指定C++標(biāo)準(zhǔn)庫 -
-o
后面指定編譯成可執(zhí)行文件的名字,不指定會(huì)默認(rèn)編譯成a.out
(ps:其實(shí)這里的a.out
是Unix系統(tǒng)很早版本的可執(zhí)行文件格式后綴养筒,感興趣可以查下相關(guān)資料)
這兩個(gè)編譯配置在Xcode里面也能找到
現(xiàn)在已經(jīng)編譯出來了可執(zhí)行文件了(名字為 linkmap)曾撤,讓我們一起開心的使用,我的linkmap 可執(zhí)行文件放在 Documents 目錄下
# 1. cd 到 linkmap可執(zhí)行文件目錄下
cd ~/Documents
# 2. 使用 linkmap 計(jì)算程序
./linkmap LinkMap.txt
NO晕粪!這不是我們想要的挤悉,每次還要cd到可執(zhí)行文件對(duì)應(yīng)的目錄下,才能使用該指令巫湘,我在其他目錄下就不能使用了嗎装悲?我想要隨心所欲地用昏鹃,任何地方打開終端就可以用。這個(gè)時(shí)候我讓我想到了環(huán)境變量PATH
衅斩,想到咱就來盆顾,沒有一絲猶豫
首先回顧下Mac系統(tǒng)默認(rèn)配置文件加載順序:/etc/profile
/etc/paths
~/.bash_profile
~/.bash_login
~/.profile
~/.bashrc
,其中/etc/profile
和/etc/paths
是系統(tǒng)級(jí)別的畏梆,系統(tǒng)啟動(dòng)就會(huì)加載您宪,后面三個(gè)是當(dāng)前用戶級(jí)的環(huán)境變量。如果~/.bash_profile
文件存在奠涌,則后面的兩個(gè)文件就會(huì)被忽略不讀了宪巨,如果~/.bash_profile
文件不存在,才會(huì)以此類推讀取后面的文件溜畅。~/.bashrc
沒有上述規(guī)則捏卓,它是bash shell打開的時(shí)候載入的。
如果讓我來選擇配置環(huán)境變量慈格,肯定是放在~/.bash_profile
文件里面嘍怠晴,但是因?yàn)槲业碾娔X安裝了zsh
,導(dǎo)致~/.bash_profile
不會(huì)被執(zhí)行浴捆,而是執(zhí)行~/.zshrc
蒜田,所以這里我會(huì)放到~/.zshrc
里面去配置環(huán)境變量,當(dāng)然如果你安裝了zsh
选泻,同時(shí)又在 ~/.zshrc
執(zhí)行了~/.bash_profile
(source ~/.bash_profile
)冲粤,那也可以配置在~/.bash_profile
里面
接下來正式開始環(huán)境變量配置工作:
1、首先我們?cè)谟脩裟夸浵聞?chuàng)建一個(gè) mybin 目錄專門用來存放我們自己寫的程序指令
mkdir ~/mybin
2页眯、將我們編譯好的 linkmap 可執(zhí)行文件放到新建好的~/mybin
目錄下
3梯捕、配置環(huán)境變量
將如下代碼加入到對(duì)應(yīng)的配置文件(ps:我的電腦因安裝了zsh
,所以要在~/.zshrc
文件里面配置窝撵,如果沒有安裝zsh
傀顾,需要在~/.bash_profile
文件里面配置)
PATH=$PATH:~/mybin
export PATH
最后執(zhí)行source ~/.zshrc
或重啟終端即可,這樣我就可以在地方打開終端碌奉,隨用所欲使用linkmap指令啦短曾。
看下效果,我cd到不同目錄下去調(diào)用linkmap指令道批,都能識(shí)別到错英。效果如下入撒,舒服了隆豹!舒服了!
用起來方便茅逮、快捷璃赡、高效判哥!滿意!5锟肌塌计!