วิธีใช้คำสั่ง awk บน Linux
บน Linux awk
เป็นไดนาโมจัดการข้อความบรรทัดคำสั่งเช่นเดียวกับภาษาสคริปต์ที่มีประสิทธิภาพ นี่คือบทแนะนำเกี่ยวกับคุณสมบัติที่ยอดเยี่ยมที่สุด
มีชื่อแค่ไหน
awk
คำสั่งได้รับการตั้งชื่อโดยใช้ชื่อย่อในสามของคนที่เขียนรุ่นเดิมในปี 1977 นี้: อัลเฟรด Aho ปีเตอร์ไวน์เบอร์เกอร์และไบรอัน Kernighan ชายสามคนนี้มาจากตำนานแพนธีออนของ AT&T Bell Laboratories Unix ด้วยการมีส่วนร่วมของผู้อื่นมากมายตั้งแต่นั้นมาก็awk
มีการพัฒนาอย่างต่อเนื่อง
เป็นภาษาสคริปต์เต็มรูปแบบรวมถึงชุดเครื่องมือจัดการข้อความที่สมบูรณ์สำหรับบรรทัดคำสั่ง หากบทความนี้กระตุ้นความอยากอาหารของคุณคุณสามารถตรวจสอบทุกรายละเอียดเกี่ยวกับ awk
การทำงานของบทความนี้
กฎรูปแบบและการดำเนินการ
awk
ทำงานกับโปรแกรมที่มีกฎซึ่งประกอบด้วยรูปแบบและการกระทำ การดำเนินการจะดำเนินการกับข้อความที่ตรงกับรูปแบบ รูปแบบอยู่ในวงเล็บปีกกา ( {}
) รูปแบบและการกระทำรวมกันเป็นกฎ awk
โปรแกรมทั้งหมดอยู่ในเครื่องหมายคำพูดเดี่ยว ( '
)
ลองมาดูประเภทของawk
โปรแกรมที่ง่ายที่สุด ไม่มีรูปแบบดังนั้นจึงจับคู่กับทุกบรรทัดของข้อความที่ป้อนเข้าไป ซึ่งหมายความว่าการดำเนินการจะดำเนินการในทุกบรรทัด เราจะใช้มันกับผลลัพธ์จากwho
คำสั่ง
นี่คือผลลัพธ์มาตรฐานจากwho
:
Who
บางทีเราอาจไม่ต้องการข้อมูลทั้งหมด แต่ต้องการเพียงแค่ดูชื่อในบัญชี เราสามารถไพพ์เอาต์พุตจากwho
เข้าไปawk
แล้วบอกawk
ให้พิมพ์เฉพาะฟิลด์แรก
ตามค่าเริ่มต้นให้awk
ถือว่าเขตข้อมูลเป็นสตริงของอักขระที่ล้อมรอบด้วยช่องว่างจุดเริ่มต้นของบรรทัดหรือจุดสิ้นสุดของบรรทัด ช่องจะระบุด้วยเครื่องหมายดอลลาร์ ( $
) และตัวเลข ดังนั้น $1
หมายถึงฟิลด์แรกซึ่งเราจะใช้กับการprint
ดำเนินการเพื่อพิมพ์ฟิลด์แรก
เราพิมพ์สิ่งต่อไปนี้:
ใคร | awk "{พิมพ์ $ 1}"
awk
พิมพ์ฟิลด์แรกและทิ้งส่วนที่เหลือของบรรทัด
เราสามารถพิมพ์ฟิลด์ได้มากเท่าที่เราต้องการ หากเราเพิ่มลูกน้ำเป็นตัวคั่นให้ awk
พิมพ์ช่องว่างระหว่างแต่ละฟิลด์
เราพิมพ์ข้อมูลต่อไปนี้เพื่อพิมพ์เวลาที่บุคคลนั้นเข้าสู่ระบบ (ฟิลด์ที่สี่):
ใคร | awk "{พิมพ์ $ 1, $ 4}"
มีตัวระบุฟิลด์พิเศษสองสามตัว สิ่งเหล่านี้แสดงถึงบรรทัดข้อความทั้งหมดและฟิลด์สุดท้ายในบรรทัดข้อความ:
- $ 0 : แสดงทั้งบรรทัดของข้อความ
- $ 1 : แสดงถึงฟิลด์แรก
- $ 2 : แสดงถึงฟิลด์ที่สอง
- $ 7 : หมายถึงฟิลด์ที่เจ็ด
- $ 45 : แสดงถึงฟิลด์ที่ 45
- $ NF : ย่อมาจาก "จำนวนฟิลด์" และหมายถึงฟิลด์สุดท้าย
เราจะพิมพ์ข้อความต่อไปนี้เพื่อเปิดไฟล์ข้อความขนาดเล็กที่มีคำพูดสั้น ๆ ที่มาจาก Dennis Ritchie:
cat dennis_ritchie.txt
เราต้องการ awk
พิมพ์ฟิลด์แรกที่สองและฟิลด์สุดท้ายของใบเสนอราคา โปรดทราบว่าแม้ว่าจะล้อมรอบในหน้าต่างเทอร์มินัล แต่ก็เป็นเพียงข้อความบรรทัดเดียว
เราพิมพ์คำสั่งต่อไปนี้:
awk '{พิมพ์ $ 1, $ 2, $ NF}' dennis_ritchie.txt
เราไม่รู้ว่า“ ความเรียบง่าย” เป็นช่องที่ 18 ในบรรทัดข้อความและเราไม่สนใจ สิ่งที่เรารู้คือมันเป็นฟิลด์สุดท้ายและเราสามารถใช้$NF
เพื่อให้ได้คุณค่า ช่วงเวลาดังกล่าวถือเป็นอีกหนึ่งตัวละครในเนื้อหาของสนาม
การเพิ่มตัวแยกฟิลด์เอาต์พุต
คุณยังสามารถบอกawk
ให้พิมพ์อักขระเฉพาะระหว่างเขตข้อมูลแทนอักขระช่องว่างเริ่มต้น เอาต์พุตเริ่มต้นจาก date
คำสั่งนั้นแปลกไปเล็กน้อยเนื่องจากเวลาถูกวางไว้ตรงกลาง อย่างไรก็ตามเราสามารถพิมพ์ข้อมูลต่อไปนี้และใช้awk
เพื่อแยกฟิลด์ที่เราต้องการ:
วันที่
วันที่ | awk '{พิมพ์ $ 2, $ 3, $ 6}'
เราจะใช้OFS
ตัวแปร (ตัวคั่นฟิลด์เอาต์พุต) เพื่อใส่ตัวคั่นระหว่างเดือนวันและปี โปรดทราบว่าด้านล่างเราใส่คำสั่งในเครื่องหมายคำพูดเดี่ยว ( '
) ไม่ใช่วงเล็บปีกกา ( {}
):
วันที่ | awk 'OFS = "/" {พิมพ์ $ 2, $ 3, $ 6}'
วันที่ | awk 'OFS = "-" {พิมพ์ $ 2, $ 3, $ 6}'
กฎ BEGIN และ END
BEGIN
กฎจะถูกดำเนินการใด ๆ อีกครั้งก่อนจะเริ่มต้นการประมวลผลข้อความ ในความเป็นจริงมันถูกดำเนินการก่อนที่awk
จะอ่านข้อความใด ๆ END
กฎจะถูกดำเนินการหลังจากการประมวลผลทั้งหมดได้เสร็จสิ้น คุณสามารถมีหลายกฎBEGIN
และ END
กฎเหล่านั้นจะดำเนินการตามลำดับ
สำหรับตัวอย่างBEGIN
กฎของเราเราจะพิมพ์ใบเสนอราคาทั้งหมดจากdennis_ritchie.txt
ไฟล์ที่เราใช้ก่อนหน้านี้โดยมีหัวเรื่องอยู่ด้านบน
โดยพิมพ์คำสั่งนี้:
awk 'BEGIN {print "Dennis Ritchie"} {print $0}' dennis_ritchie.txt
Note the BEGIN
rule has its own set of actions enclosed within its own set of curly braces ({}
).
We can use this same technique with the command we used previously to pipe output from who
into awk
. To do so, we type the following:
who | awk 'BEGIN {print "Active Sessions"} {print $1,$4}'
Input Field Separators
If you want awk
to work with text that doesn’t use whitespace to separate fields, you have to tell it which character the text uses as the field separator. For example, the /etc/passwd
file uses a colon (:
) to separate fields.
We’ll use that file and the -F
(separator string) option to tell awk
to use the colon (:
) as the separator. We type the following to tell awk
to print the name of the user account and the home folder:
awk -F: '{print $1,$6}' /etc/passwd
The output contains the name of the user account (or application or daemon name) and the home folder (or the location of the application).
Adding Patterns
If all we’re interested in are regular user accounts, we can include a pattern with our print action to filter out all other entries. Because User ID numbers are equal to, or greater than, 1,000, we can base our filter on that information.
We type the following to execute our print action only when the third field ($3
) contains a value of 1,000 or greater:
awk -F: '$3 >= 1000 {print $1,$6}' /etc/passwd
The pattern should immediately precede the action with which it’s associated.
We can use the BEGIN
rule to provide a title for our little report. We type the following, using the (\n
) notation to insert a newline character into the title string:
awk -F: 'BEGIN {print "User Accounts\n-------------"} $3 >= 1000 {print $1,$6}' /etc/passwd
Patterns are full-fledged regular expressions, and they’re one of the glories of awk
.
Let’s say we want to see the universally unique identifiers (UUIDs) of the mounted file systems. If we search through the /etc/fstab
file for occurrences of the string “UUID,” it ought to return that information for us.
We use the search pattern “/UUID/” in our command:
awk '/UUID/ {print $0}' /etc/fstab
It finds all occurrences of “UUID” and prints those lines. We actually would’ve gotten the same result without the print
action because the default action prints the entire line of text. For clarity, though, it’s often useful to be explicit. When you look through a script or your history file, you’ll be glad you left clues for yourself.
The first line found was a comment line, and although the “UUID” string is in the middle of it, awk
still found it. We can tweak the regular expression and tell awk
to process only lines that start with “UUID.” To do so, we type the following which includes the start of line token (^
):
awk '/^UUID/ {print $0}' /etc/fstab
That’s better! Now, we only see genuine mount instructions. To refine the output even further, we type the following and restrict the display to the first field:
awk '/^UUID/ {print $1}' /etc/fstab
If we had multiple file systems mounted on this machine, we’d get a neat table of their UUIDs.
Built-In Functions
awk
has many functions you can call and use in your own programs, both from the command line and in scripts. If you do some digging, you’ll find it very fruitful.
To demonstrate the general technique to call a function, we’ll look at some numeric ones. For example, the following prints the square root of 625:
awk 'BEGIN { print sqrt(625)}'
This command prints the arctangent of 0 (zero) and -1 (which happens to be the mathematical constant, pi):
awk 'BEGIN {print atan2(0, -1)}'
In the following command, we modify the result of the atan2()
function before we print it:
awk 'BEGIN {print atan2(0, -1)*100}'
Functions can accept expressions as parameters. For example, here’s a convoluted way to ask for the square root of 25:
awk 'BEGIN { print sqrt((2+3)*5)}'
awk Scripts
If your command line gets complicated, or you develop a routine you know you’ll want to use again, you can transfer your awk
command into a script.
In our example script, we’re going to do all of the following:
- Tell the shell which executable to use to run the script.
- Prepare
awk
to use theFS
field separator variable to read input text with fields separated by colons (:
). - Use the
OFS
output field separator to tellawk
to use colons (:
) to separate fields in the output. - Set a counter to 0 (zero).
- Set the second field of each line of text to a blank value (it’s always an “x,” so we don’t need to see it).
- Print the line with the modified second field.
- Increment the counter.
- Print the value of the counter.
Our script is shown below.
The BEGIN
rule carries out the preparatory steps, while the END
rule displays the counter value. The middle rule (which has no name, nor pattern so it matches every line) modifies the second field, prints the line, and increments the counter.
The first line of the script tells the shell which executable to use (awk
, in our example) to run the script. It also passes the -f
(filename) option to awk
, which informs it the text it’s going to process will come from a file. We’ll pass the filename to the script when we run it.
We’ve included the script below as text so you can cut and paste:
#!/usr/bin/awk -f BEGIN { # set the input and output field separators FS=":" OFS=":" # zero the accounts counter accounts=0 } { # set field 2 to nothing $2="" # print the entire line print $0 # count another account accounts++ } END { # print the results print accounts " accounts.\n" }
Save this in a file called omit.awk
. To make the script executable, we type the following using chmod
:
chmod +x omit.awk
Now, we’ll run it and pass the /etc/passwd
file to the script. This is the file awk
will process for us, using the rules within the script:
./omit.awk /etc/passwd
The file is processed and each line is displayed, as shown below.
The “x” entries in the second field were removed, but note the field separators are still present. The lines are counted and the total is given at the bottom of the output.
awk Doesn’t Stand for Awkward
awk
doesn’t stand for awkward; it stands for elegance. It’s been described as a processing filter and a report writer. More accurately, it’s both of these, or, rather, a tool you can use for both of these tasks. In just a few lines, awk
achieves what requires extensive coding in a traditional language.
That power is harnessed by the simple concept of rules that contain patterns, that select the text to process, and actions that define the processing.