如何使用AWK語言在Linux中操縱文本

介紹

Linux 實用工具通常遵循 Unix 設計哲學。鼓勵工具小巧、使用純文本文件作為輸入和輸出,以模塊化方式運作。因此,我們在像 sedawk 這樣的工具中擁有了強大的文本處理功能。

awk 同時是一種編程語言和文本處理器,您可以使用它以非常有用的方式操作文本數據。在本指南中,您將探索如何使用 awk 命令行工具以及如何使用它來處理文本。

基本語法

awk 命令已經包含在所有現代 Linux 系統中,因此您無需安裝即可開始使用它。

awk 在處理格式化方式可預測的文本文件時最為有用。例如,它非常擅長解析和操作表格數據。它以逐行方式操作整個文件。

默認情況下,它使用空白(空格、制表符等)來分隔字段。幸運的是,您的 Linux 系統上許多配置文件都使用這種格式。

awk 命令的基本格式是:

  1. awk '/search_pattern/ { action_to_take_on_matches; another_action; }' file_to_parse

您可以从任何 awk 命令中省略搜索部分或操作部分之一。如果未给出“操作”部分,则默认执行的操作是“打印”。这只是打印所有匹配的行。

如果未给出搜索部分,则 awk 在每一行上执行列出的操作。

如果两者都给出,awk 使用搜索部分来决定当前行是否反映模式,然后在匹配时执行操作。

在其最简单的形式中,您可以像使用 cat 一样使用 awk 将文本文件的所有行打印到屏幕上。

创建一个名为 favorite_food.txt 的文件,其中列出了一组朋友的最爱食物:

  1. echo "carrot sandy
  2. wasabi luke
  3. sandwich brian
  4. salad ryan
  5. spaghetti jessica" > favorite_food.txt

现在使用 awk 命令将文件打印到屏幕上:

  1. awk '{print}' favorite_food.txt

您将看到文件打印到屏幕上:

Output
carrot sandy wasabi luke sandwich brian salad ryan spaghetti jessica

这不是很有用。让我们尝试一下 awk 的搜索过滤功能,通过搜索包含文本“sand”的文件:

  1. awk '/sand/' favorite_food.txt
Output
carrot sandy sandwich brian

如您所见,awk 现在只打印包含字符“sand”的行。

使用正则表达式,您可以针对文本的特定部分。要仅显示以字母“sand”开头的行,请使用正则表达式 ^sand

  1. awk '/^sand/' favorite_food.txt

这次,只显示了一行:

Output
sandwich brian

类似地,您可以使用操作部分来指定要打印的信息片段。例如,要仅打印第一列,请使用以下命令:

  1. awk '/^sand/ {print $1;}' favorite_food.txt
Output
sandwich

您可以通过与其列号相关联的变量引用每一列(由空白分隔)。例如,第一列是$1,第二列是$2,您可以用$0引用整行。

內部變量和擴展格式

在處理文件時,awk命令使用一些內部變量來分配某些信息。

  • FILENAME:引用當前輸入文件。
  • FNR:相對於當前輸入文件的當前記錄號。例如,如果您有兩個輸入文件,則此命令將告訴您每個文件的記錄號,而不是作為總計。
  • FS:用於表示記錄中每個字段的當前字段分隔符號。默認情況下,此設置為空格。
  • NF:當前記錄中的字段數。
  • NR:當前記錄的編號。
  • OFS:輸出數據的字段分隔符號。默認情況下,此設置為空格。
  • ORS:輸出數據的記錄分隔符號。默認情況下,這是一個換行符號。
  • RS:用於區分輸入文件中不同記錄的記錄分隔符。默認情況下,這是一個換行符。

您可以隨意更改這些變量的值,以滿足您文件的需求。通常,您在處理的初始化階段這樣做。

這將我們帶到另一個重要概念。 awk 語法比您迄今使用的稍微複雜一些。還有可選的 BEGINEND 塊,可以包含在文件處理之前和之後分別執行的命令。

這使我們擴展的語法看起來像這樣:

  1. awk 'BEGIN { action; }
  2. /search/ { action; }
  3. END { action; }' input_file

BEGINEND 關鍵字是特定的條件集,就像搜索參數一樣。它們在文檔處理之前和之後匹配。

這意味著您可以在 BEGIN 部分更改一些內部變量。例如,/etc/passwd 文件是使用冒號 (:) 而不是空格分隔的。

要打印此文件的第一列,請執行以下命令:

  1. awk 'BEGIN { FS=":"; }
  2. { print $1; }' /etc/passwd
Output
root daemon bin sys sync games man . . .

您可以使用 BEGINEND 塊打印有關您正在打印的字段的信息。使用以下命令將文件中的數據轉換為一個表,使用制表符 \t 進行漂亮的間隔:

  1. awk 'BEGIN { FS=":"; print "User\t\tUID\t\tGID\t\tHome\t\tShell\n--------------"; }
  2. {print $1,"\t\t",$3,"\t\t",$4,"\t\t",$6,"\t\t",$7;}
  3. END { print "---------\nFile Complete" }' /etc/passwd

您將看到此輸出:

Output
User UID GID Home Shell -------------- root 0 0 /root /bin/bash daemon 1 1 /usr/sbin /bin/sh bin 2 2 /bin /bin/sh sys 3 3 /dev /bin/sh sync 4 65534 /bin /bin/sync . . . --------- File Complete

正如您所看到的,您可以通過利用一些 awk 的功能來很好地格式化事物。

每個擴展部分都是可選的。 事實上,如果定義了另一個部分,則主動作部分本身也是可選的。 例如,您可以這樣做:

  1. awk 'BEGIN { print "We can use awk like the echo command"; }'

,然後您將看到此輸出:

Output
We can use awk like the echo command

現在讓我們看看如何在輸出的字段中查找文本。

字段搜索和復合表達式

在先前的示例中,您打印了以“sand”開頭的favorite_food.txt文件中的行。 這很容易,因為您要查找整行的開頭。

如果您想要查找搜索模式是否匹配字段的開頭,怎麼辦?字段

創建一個新版本的favorite_food.txt文件,其中在每個人的食物前面添加一個項目號:

  1. echo "1 carrot sandy
  2. 2 wasabi luke
  3. 3 sandwich brian
  4. 4 salad ryan
  5. 5 spaghetti jessica" > favorite_food.txt

如果您想要查找此文件中以“sa”開頭的所有食物,您可以開始嘗試類似這樣的事物:

  1. awk '/sa/' favorite_food.txt

顯示包含“sa”的所有行:

Output
1 carrot sandy 2 wasabi luke 3 sandwich brian 4 salad ryan

在這裡,您正在匹配單詞中的任何“sa”實例。 這最終包括了像“wasabi”這樣的東西,其中模式在中間,或者“sandy”這樣的東西,它不在您想要的列中。 在這種情況下,您只對以“sa”開頭的單詞感興趣,在第二列。

你可以使用以下命令告訴awk只匹配第二列的開頭:

  1. awk '$2 ~ /^sa/' favorite_food.txt

正如你所見,這允許我們只在第二列的開頭搜索匹配項。

field_num ~部分指定awk只應關注第二列。

Output
3 sandwich brian 4 salad ryan

你也可以輕鬆地搜索不匹配的內容,只需在波浪號(~)前加上“!”字符即可。這個命令將返回所有不以“sa”開頭的行:

  1. awk '$2 !~ /^sa/' favorite_food.txt
Output
1 carrot sandy 2 wasabi luke 5 spaghetti jessica

如果你後來決定只對不以“sa”開頭且項目號碼小於5的行感興趣,你可以使用類似這樣的複合表達式:

  1. awk '$2 !~ /^sa/ && $1 < 5' favorite_food.txt

這引入了一些新概念。首先是使用&&運算符添加額外條件以使行匹配。使用這個,你可以結合任意數量的條件來使行匹配。在這種情況下,你使用這個運算符添加了一個檢查第一列值是否小於5的條件。

你將看到這個輸出:

Output
1 carrot sandy 2 wasabi luke

你可以使用awk處理文件,但也可以處理其他程序的輸出。

處理其他程序的輸出

您可以使用 awk 命令來解析其他程序的輸出,而不是指定文件名。例如,您可以使用 awkip 命令中解析出 IPv4 地址。

ip a 命令顯示有關您計算機上所有網絡接口的 IP 地址、廣播地址和其他信息。要顯示名為 eth0 的接口的信息,請使用此命令:

  1. ip a s eth0

您將看到以下結果:

Output
2571: eth0@if2572: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.11/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever

您可以使用 awk 來定位 inet 行,然後僅打印出 IP 地址:

  1. ip a s eth0 | awk -F '[\/ ]+' '/inet / {print $3}'

-F 標志告訴 awk 使用正則表達式 [\/ ]+ 來按斜線或空格分隔。這將將行 inet 172.17.0.11/16 拆分為單獨的字段。IP 地址位於第三個字段,因為行首的空格也被計為一個字段,因為您同時按斜線和空格分隔。請注意,在這種情況下,awk 將連續的空格視為單個空格。

輸出顯示了 IP 地址:

Output
172.17.0.11

您將發現許多地方都可以使用 awk 來搜索或解析其他命令的輸出。

結論

到目前为止,您应该已经基本了解了如何使用 awk 命令来操作、格式化和选择性地打印文本文件和文本流。然而,Awk 是一个更大的主题,实际上是一种完整的编程语言,包括变量赋值、控制结构、内置函数等等。您可以在自己的脚本中使用它来可靠地格式化文本。

要了解更多关于 awk 的信息,您可以阅读由其创作者撰写的免费公共领域书籍,其中详细介绍了更多内容。

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-the-awk-language-to-manipulate-text-in-linux