Linux 命令 awk 詳解
awk簡介
awk其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan姓氏的首個字母。實際上 AWK 的確擁有自己的語言: AWK 程式設計語言,三位建立者已將它正式定義為“樣式掃描和處理語言”。它允許您建立簡短的程式,這些程式讀取輸入檔案、為資料排序、處理資料、對輸入執行計算以及生成報表,還有無數其他的功能。
awk 是一種很棒的語言,它適合文字處理和報表生成,其語法較為常見,借鑑了某些語言的一些精華,如 C 語言等。在 linux 系統日常處理工作中,發揮很重要的作用,掌握了 awk將會使你的工作變的高大上。 awk 是三劍客的老大,利劍出鞘,必會不同凡響。
使用方法
1 | awk '{pattern + action}' {filenames} |
儘管操作可能會很複雜,但語法總是這樣,其中 pattern 表示 AWK 在資料中查詢的內容,而 action 是在找到匹配內容時所執行的一系列命令。花括號({})不需要在程式中始終出現,但它們用於根據特定的模式對一系列指令進行分組。 pattern就是要表示的正則表示式,用斜槓括起來。
awk語言的最基本功能是在檔案或者字串中基於指定規則瀏覽和抽取資訊,awk抽取資訊後,才能進行其他文字操作。完整的awk指令碼通常用來格式化文字檔案中的資訊。
通常,awk是以檔案的一行為處理單位的。awk每接收檔案的一行,然後執行相應的命令,來處理文字。
awk 的原理
通過一個簡短的命令,我們來了解其工作原理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [[email protected] scripts]# awk '{print $0}' /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin ..................................................... [[email protected] scripts]# echo hhh|awk '{print "hello,world"}' hello,world [[email protected] scripts]# awk '{print "hiya"}' /etc/passwd hiya hiya hiya hiya ............................................... |
你將會見到/etc/passwd 檔案的內容出現在眼前。現在,解釋 awk 做了些什麼。呼叫 awk時,我們指定/etc/passwd 作為輸入檔案。執行 awk 時,它依次對/etc/passwd 中的每一行執行 print 命令。
所有輸出都發送到 stdout,所得到的結果與執行 cat /etc/passwd 完全相同。現在,解釋{ print }程式碼塊。在 awk 中,花括號用於將幾塊程式碼組合到一起,這一點類似於 C 語言。在程式碼塊中只有一條 print 命令。在 awk 中,如果只出現 print 命令,那麼將列印當前行的全部內容。再次說明, awk 對輸入檔案中的每一行都執行這個指令碼。
1 2 3 4 | $ awk -F":" '{ print $1 }' /etc/passwd $ awk -F":" '{ print $1 $3 }' /etc/passwd $ awk -F":" '{ print $1 " " $3 }' /etc/passwd $ awk -F":" '{ print "username: " $1 "\t\tuid:" $3" }' /etc/passwd |
-F引數:指定分隔符,可指定一個或多個
print 後面做字串的拼接
下面通過幾例項來了解下awk的工作原理:
例項一:只檢視test.txt檔案(100行)內第20到第30行的內容(企業面試)
1 2 3 4 5 6 7 8 9 10 11 12 | [[email protected] scripts]# awk '{if(NR>=20 && NR<=30) print $1}' test.txt 20 21 22 23 24 25 26 27 28 29 30 |
例項二:已知test.txt檔案內容為:
1 2 | [[email protected] scripts]# cat test.txt I am Poe,my qq is 33794712 |
請從該檔案中過濾出'Poe'字串與33794712,最後輸出的結果為:Poe 33794712
1 2 | [[email protected] scripts]# awk -F '[ ,]+' '{print $3" "$7}' test.txt Poe 33794712 |
BEGIN 和 END 模組
通常,對於每個輸入行, awk 都會執行每個指令碼程式碼塊一次。然而,在許多程式設計情況中,可能需要在 awk 開始處理輸入檔案中的文字之前執行初始化程式碼。對於這種情況, awk 允許您定義一個 BEGIN 塊。
因為 awk 在開始處理輸入檔案之前會執行 BEGIN 塊,因此它是初始化 FS(欄位分隔符)變數、列印頁首或初始化其它在程式中以後會引用的全域性變數的極佳位置。
awk 還提供了另一個特殊塊,叫作 END 塊。 awk 在處理了輸入檔案中的所有行之後執行這個塊。通常, END 塊用於執行最終計算或列印應該出現在輸出流結尾的摘要資訊。
例項一:統計/etc/passwd的賬戶人數
1 2 3 4 | [[email protected] scripts]# awk '{count++;print $0;} END{print "user count is ",count}' passwd root:x:0:0:root:/root:/bin/bash .............................................. user count is 27 |
count是自定義變數。之前的action{}裡都是隻有一個print,其實print只是一個語句,而action{}可以有多個語句,以;號隔開。這裡沒有初始化count,雖然預設是0,但是妥當的做法還是初始化為0:
1 2 3 4 5 | [[email protected] scripts]# awk 'BEGIN {count=0;print "[start] user count is ",count} {count=count+1;print $0} END{print "[end] user count is ",count}' passwd [start] user count is 0 root:x:0:0:root:/root:/bin/bash ................................................................... [end] user count is 27 |
例項二:統計某個資料夾下的檔案佔用的位元組數
1 2 | [[email protected] scripts]# ll |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ",size}' [end]size is 1489 |
如果以M為單位顯示:
1 2 | [[email protected] scripts]# ll |awk 'BEGIN{size=0;} {size=size+$5;} END{print "[end]size is ",size/1024/1024,"M"}' [end]size is 0.00142002 M |
awk運算子
awk 賦值運算子:a+5;等價於: a=a+5;其他同類
1 2 | [[email protected] scripts]# awk 'BEGIN{a=5;a+=5;print a}' 10 |
awk邏輯運算子:
1 2 | [[email protected] scripts]# awk 'BEGIN{a=1;b=2;print (a>2&&b>1,a=1||b>1)}' 0 1 |
判斷表示式 a>2&&b>1為真還是為假,後面的表示式同理
awk正則運算子:
1 2 | [[email protected] scripts]# awk 'BEGIN{a="100testaa";if(a~/100/) {print "ok"}}' ok |
1 2 | [[email protected] scripts]# echo|awk 'BEGIN{a="100testaaa"}a~/test/{print "ok"}' ok |
關係運算符:
如: >< 可以作為字串比較,也可以用作數值比較,關鍵看運算元如果是字串就會轉換為字串比較。兩個都為數字才轉為數值比較。字串比較:按照ascii碼順序比較。
1 2 3 4 5 | [[email protected] scripts]# awk 'BEGIN{a="11";if(a>=9){print "ok"}}' #無輸出 [[email protected] scripts]# awk 'BEGIN{a=11;if(a>=9){print "ok"}}' ok [[email protected] scripts]# awk 'BEGIN{a;if(a>=b){print "ok"}}' ok |
awk 算術運算子:
說明,所有用作算術運算子進行操作,運算元自動轉為數值,所有非數值都變為0。
1 2 3 4 | [[email protected] scripts]# awk 'BEGIN{a="b";print a++,++a}' 0 2 [[email protected] scripts]# awk 'BEGIN{a="20b4";print a++,++a}' 20 22 |
這裡的a++ , ++a與javascript語言一樣:a++是先賦值加++;++a是先++再賦值
三目運算子 ?:
1 2 3 4 | [[email protected] scripts]# awk 'BEGIN{a="b";print a=="b"?"ok":"err"}' ok [[email protected] scripts]# awk 'BEGIN{a="b";print a=="c"?"ok":"err"}' err |
常用 awk 內建變數
注:內建變數很多,參閱相關資料
欄位分隔符 FS
FS="\t" 一個或多個 Tab 分隔
1 2 3 4 | [[email protected] scripts]# cat tab.txt ww CC IDD [[email protected] scripts]# awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' tab.txt ww CC IDD |
FS="[[:space:]+]" 一個或多個空白空格,預設的
1 2 3 4 5 6 | [[email protected] scripts]# cat space.txt we are studing awk now! [[email protected] scripts]# awk -F [[:space:]+] '{print $1,$2,$3,$4,$5}' space.txt we are [[email protected] scripts]# awk -F [[:space:]+] '{print $1,$2}' space.txt we are |
FS="["":]+" 以一個或多個空格或:分隔
1 2 3 4 | [[email protected] scripts]# cat hello.txt root:x:0:0:root:/root:/bin/bash [[email protected] scripts]# awk -F [" ":]+ '{print $1,$2,$3}' hello.txt root x 0 |
欄位數量 NF
1 2 3 4 5 | [[email protected] scripts]# cat hello.txt root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin:888 [[email protected] scripts]# awk -F ":" 'NF==8{print $0}' hello.txt bin:x:1:1:bin:/bin:/sbin/nologin:888 |
記錄數量 NR
1 2 | [[email protected] scripts]# ifconfig eth0|awk -F [" ":]+ 'NR==2{print $4}' ## NR==2也就是取第2行 192.168.17.129 |
RS 記錄分隔符變數將 FS 設定成"\n"告訴 awk 每個欄位都佔據一行。通過將 RS 設定成"",還會告訴 awk每個地址記錄都由空白行分隔。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | [[email protected] scripts]# cat recode.txt Jimmy the Weasel 100 Pleasant Drive San Francisco,CA 123456 Big Tony 200 Incognito Ave. Suburbia,WA 64890 [[email protected] scripts]# cat awk.txt #!/bin/awk BEGIN { FS="\n" RS="" } { print $1","$2","$3 } [[email protected] scripts]# awk -f awk.txt recode.txt Jimmy the Weasel,100 Pleasant Drive,San Francisco,CA 123456 Big Tony,200 Incognito Ave.,Suburbia,WA 64890 |
OFS 輸出欄位分隔符
1 2 3 4 5 6 7 8 9 | [[email protected] scripts]# cat hello.txt root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin:888 [[email protected] scripts]# awk 'BEGIN{FS=":"}{print $1","$2","$3}' hello.txt root,x,0 bin,x,1 [[email protected] scripts]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' hello.txt root#x#0 bin#x#1 |
ORS 輸出記錄分隔符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | [[email protected] scripts]# cat recode.txt Jimmy the Weasel 100 Pleasant Drive San Francisco,CA 123456 Big Tony 200 Incognito Ave. Suburbia,WA 64890 [[email protected] scripts]# cat awk.txt #!/bin/awk BEGIN { FS="\n" RS="" ORS="\n\n" } { print $1","$2","$3 } [[email protected] scripts]# awk -f awk.txt recode.txt Jimmy the Weasel,100 Pleasant Drive,San Francisco,CA 123456 Big Tony,200 Incognito Ave.,Suburbia,WA 64890 |
awk 正則
正則應用
規則表示式
awk'/REG/{action} ' file,/REG/為正則表示式,可以將$0 中,滿足條件的記錄送入到:action 進行處理
1 2 3 4 5 6 7 8 9 | [[email protected] scripts]# awk '/root/{print $0}' passwd ##匹配所有包含root的行 root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [[email protected] scripts]# awk -F: '$5~/root/{print $0}' passwd ## 以分號作為分隔符,匹配第5個欄位是root的行 root:x:0:0:root:/root:/bin/bash [[email protected] scripts]# ifconfig eth0|awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print $4}' 192.168.17.129 |
布林表示式
awk '布林表示式{action}' file 僅當對前面的布林表示式求值為真時, awk 才執行程式碼塊。
1 2 3 4 | [[email protected] scripts]# awk -F: '$1=="root"{print $0}' passwd root:x:0:0:root:/root:/bin/bash [[email protected] scripts]# awk -F: '($1=="root")&&($5=="root") {print $0}' passwd root:x:0:0:root:/root:/bin/bash |
awk 的 if、迴圈和陣列
條件語句
awk 提供了非常好的類似於 C 語言的 if 語句。
1 2 3 4 5 6 7 8 9 10 11 12 13 | { if ($1=="foo"){ if($2=="foo"){ print "uno" }else{ print "one" } }elseif($1=="bar"){ print "two" }else{ print "three" } } |
使用 if 語句還可以將程式碼:
1 | ! /matchme/ { print $1 $3 $4 } |
轉換成:
1 2 3 4 5 | { if ( $0 !~ /matchme/ ) { print $1 $3 $4 } } |
迴圈結構
我們已經看到了 awk 的 while 迴圈結構,它等同於相應的 C 語言 while 迴圈。 awk 還有"do...while"迴圈,它在程式碼塊結尾處對條件求值,而不像標準 while 迴圈那樣在開始處求值。
它類似於其它語言中的"repeat...until"迴圈。以下是一個示例:
do...while 示例
1 2 3 4 5 | { count=1do { print "I get printed at least once no matter what" } while ( count !=1 ) } |
與一般的 while 迴圈不同,由於在程式碼塊之後對條件求值, "do...while"迴圈永遠都至少執行一次。換句話說,當第一次遇到普通 while 迴圈時,如果條件為假,將永遠不執行該迴圈。
for 迴圈
awk 允許建立 for 迴圈,它就象 while 迴圈,也等同於 C 語言的 for 迴圈:
1 2 3 | for ( initial assignment; comparison; increment ) { code block } |
以下是一個簡短示例:
1 2 3 | for ( x=1;x<=4;x++ ) { print "iteration", x } |
此段程式碼將列印:
1 2 3 4 | iteration1 iteration2 iteration3 iteration4 |
break 和 continue
此外,如同 C 語言一樣, awk 提供了 break 和 continue 語句。使用這些語句可以更好地控制 awk 的迴圈結構。以下是迫切需要 break 語句的程式碼片斷:
1 2 3 4 5 | while 死迴圈 while (1) { print "forever and ever..." } while 死迴圈 1 永遠代表是真,這個 while 迴圈將永遠執行下去。 |
以下是一個只執行十次的迴圈:
1 2 3 4 5 6 7 8 9 | #break 語句示例 x=1 while(1) { print "iteration", x if ( x==10 ) { break } x++ } |
這裡, break 語句用於“逃出”最深層的迴圈。 "break"使迴圈立即終止,並繼續執行迴圈程式碼塊後面的語句。
continue 語句補充了 break,其作用如下:
1 2 3 4 5 6 7 8 9 10 11 | x=1while (1) { if ( x==4 ) { x++ continue } print "iteration", x if ( x>20 ) { break } x++ } |
這段程式碼列印"iteration1"到"iteration21", "iteration4"除外。如果迭代等於 4,則增加 x並呼叫 continue 語句,該語句立即使 awk 開始執行下一個迴圈迭代,而不執行程式碼塊的其餘部分。如同 break 一樣,
continue 語句適合各種 awk 迭代迴圈。在 for 迴圈主體中使用時, continue 將使迴圈控制變數自動增加。以下是一個等價迴圈:
1 2 3 4 5 6 | for ( x=1;x<=21;x++ ) { if ( x==4 ) { continue } print "iteration", x } |
在while 迴圈中時,在呼叫 continue 之前沒有必要增加 x,因為 for 迴圈會自動增加 x。
陣列
AWK 中的陣列都是關聯陣列,數字索引也會轉變為字串索引
1 2 3 4 5 6 7 8 9 10 11 | { cities[1]=”beijing” cities[2]=”shanghai” cities[“three”]=”guangzhou” for( c in cities) { print cities[c] } print cities[1] print cities[“1”] print cities[“three”] } |
for…in 輸出,因為陣列是關聯陣列,預設是無序的。所以通過 for…in 得到是無序的陣列。如果需要得到有序陣列,需要通過下標獲得。
陣列的典型應用
用 awk 中檢視伺服器連線狀態並彙總
1 2 3 | netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}' ESTABLISHED 1 LISTEN 20 |
統計 web 日誌訪問流量,要求輸出訪問次數,請求頁面或圖片,每個請求的總大小,總訪問流量的大小彙總
1 2 3 4 5 6 7 8 9 | awk '{a[$7]+=$10;++b[$7];total+=$10}END{for(x in a)print b[x],x,a[x]|"sort -rn -k1";print "total size is :"total}' /app/log/access_log total size is :172230 21 /icons/poweredby.png 83076 14 / 70546 8 /icons/apache_pb.gif 18608 a[$7]+=$10 表示以第 7 列為下標的陣列( $10 列為$7 列的大小),把他們大小累加得到 $7 每次訪問的大小,後面的 for 迴圈有個取巧的地方, a 和 b 陣列的下標相同,所以一 條 for 語句足矣 |
常用字串函式
字串函式的應用
替換
1 2 3 | awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}' this is a test!test! 在 info 中查詢滿足正則表示式, /[0-9]+/ 用”!”替換,並且替換後的值,賦值給 info 未 給 info 值,預設是$0 |
查詢
1 2 | awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}' ok #未找到,返回 0 |
匹配查詢
1 2 | awk 'BEGIN{info="this is a test2010test!";print match(info,/[0-9]+/)?"ok":"no found";}' ok #如果查詢到數字則匹配成功返回 ok,否則失敗,返回未找到 |
擷取
1 2 | awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}' s is a tes #從第 4 個 字元開始,擷取 10 個長度字串 |
分割
1 2 3 4 | awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}' 4 4 test 1 this 2 is 3 a #分割 info,動態建立陣列 tA,awk for …in 迴圈,是一個無序的迴圈。 並不是從陣列下標 1…n 開始 |
引用:http://www.cnblogs.com/ginvip/p/6539393.html
細節影響成敗,感謝 琴酒網路 的總結整理。
相關推薦
Linux 命令 awk 詳解
awk簡介awk其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan姓氏的首個字母。實際上 AWK 的確擁有自己的語言: AWK 程式設計語言,三位建立者已將它正式定義為“樣式掃描和處理語言”。它允許您建立簡短的
99%的程式設計師都會實用到的Linux命令—— grep詳解
grep 是Linux系統非常強大的基本行的文字檢索工具,以下為常用的 grep命令: -i 不區分大小 -c 統計匹配行數 -n 輸出行號 -v 反向匹配 -m 指定個數 –color 顏色突出 -A 匹配後多少行 -B 匹
菜鳥級別的一些最基礎的linux命令例項詳解
1、剛連線到虛擬機器,如下圖,劃紅線的‘~’表示進入的你這個使用者的當前目錄,瞭解下,作用不大,每條命令輸入完成後都是按enter鍵確定: 2、首先進入到根目錄,根目錄中是囊括了這臺機子裡所有的東西,命令是:cd /,如下,劃線部分已經變成了/,表示進入根目錄了: 3、
linux 命令 lsof 詳解
簡介lsof(list open files)是一個列出當前系統開啟檔案的工具。在linux環境下,任何事物都以檔案的形式存在,通過檔案不僅僅可以訪問常規資料,還可以訪問網路連線和硬體。所以如傳輸控制協議 (TCP) 和使用者資料報協議 (UDP) 套接字等,系統在後臺都為該
linux命令top詳解
top命令是用來監控linux系統的執行狀況,如CPU,記憶體的使用情況。對於結果的資料有些不太清楚,在此詳細的記錄下 上圖是執行top命令的結果圖,對比上圖進行分析。 第一行 top -17:20:04 | up 13 days, 3:1
linux 命令 tr 詳解
[email protected]:~$ tr --help Usage: tr [OPTION]... SET1 [SET2] Translate, squeeze, and/or delete characters from standard input, writing to stand
linux 命令 route 詳解
Destination 目標 The destination network or destination host. 目標網路或目標主機。 Gateway 閘道器 The gateway address or '*' if none set.
Linux vmstat命令實戰詳解
上下文切換 span 一次 過多 sta log 讀取文件 負載 監控 vmstat命令是最常見的Linux/Unix監控工具,可以展現給定時間間隔的服務器的狀態值,包括服務器的CPU使用率,內存使用,虛擬內存交換情況,IO讀寫情況。這個命令是我查看Linux/Unix最喜
linux流量監控iftop命令安裝詳解
libpcap 安裝 流量 指定 evel .tar.gz 之前 完成 configure iftop跟nload差不多,也是捕獲網卡流量的命令,nload的安裝見之前發布的教程:http://www.cnblogs.com/catlee/p/5703541.html 開
awk命令例子詳解
劃線 pan $1 空格 led 部分 分隔 線表 模式 awk -F: ‘{print "Number of dields: "NF}‘ passwd 字段分隔符設為冒號,所以每條記錄的字段數變成7; awk ‘{print "Number of diel
LINUX下分區命令Parted詳解
運行 script mbr gpt分區 ble 版本號 may was mkpart 通常劃分分區工具我們用的比較多是fdisk命令,但是現在由於磁盤越來越廉價,而且磁盤空間越來越大。而fdisk工具他對分區是有大小限制的,它只能劃分小於2T的磁盤。現在的磁盤
linux中cat、more、less命令區別詳解
more|less本文轉自:http://blog.csdn.net/xyw_blog/article/details/16861681在《Python絕技》這本書的第一個小程序首先展示了針對與unix系統中shadow文件密碼的暴力破解的能力,因為之前只是對shadow文件停留在保存了用戶密碼的階段,但並沒
Linux文本處理命令“awk”簡解
linux awk awk 格式: awk [option] ‘條件1{動作1}條件2{動作2}...‘ 文件名 條件:正則表達式;邏輯判斷式 動作:格式化輸出;流程控制語句 選項: -F 指定分割符 變量: $1 $2 ... 第某列 NR 行號
(轉)linux nc命令使用詳解
遠程拷貝 工作 主機 lin con gin browser -s 反向連接 linux nc命令使用詳解 原文:https://www.2cto.com/os/201306/220971.html 功能說明:功能強大的網絡工具 語 法:nc [-hlnruz][-g&
(轉)linux paste命令用法詳解
--help 分類 chroot 幫助 情況下 alias query 技術分享 一次 linux paste命令用法詳解原文:http://www.xfcodes.com/linuxcmd/mulu/10211.htmlinux下paste命令,可用於合並文件的列。 功能
[svc]Linux vmstat命令實戰詳解
cpu 內存 磁盤 性能 介紹 給定 不足 code 大量 進程阻塞 log vmstat輸出 註:是cpu 內存 磁盤 虛擬內存交換情況 io讀寫情況 vmstat命令是最常見的Linux/Unix監控工具,可以展現給定時間間隔的服務器的狀態值,包括服務器的CPU使用率,
linux awk詳解
自然 小寫 功能 () 取字符 求和 不同 pan 詳解 awk: awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯, awk在其對數據分析並生成報告時,顯得尤為強大。 簡單來說awk就是把文件逐行的讀入,以空格為默認分隔符將
Linux常用命令示例詳解(一)-檔案管理
1、touch 命令 使用說明: Linux touch命令用於修改檔案或者目錄的時間屬性,包括存取時間和更改時間。若檔案不存在,系統會建立一個新的檔案。 使用語法: touch [-acfm][-d<日期時間>][-r<參考檔案或目錄>
Linux系列之SAR命令使用詳解
1. CPU利用率 sar -p (檢視全天) sar -u 1 10 (1:每隔一秒,10:寫入10次) 1.1. CPU輸出項說明 輸出項 詳細說明 CPU all 表示統計資訊為
Linux的dd命令使用詳解
http://blog.sina.com.cn/s/blog_8b5bb24f01016y3o.html 一、dd命令的解釋 dd:用指定大小的塊拷貝一個檔案,並在拷貝的同時進行指定的轉換。 注意:指定數字的地方若以下列字元結