Updated: 18 October 2026 · Applies to: gawk 5.2.1 (Debian 13) and 5.3.0 (AlmaLinux 10.2), mawk 1.3.4 (Debian 13, Ubuntu 24.04 and 26.04) and BusyBox 1.37.0 awk, tested
To run awk on a file, put the file name after the program: awk '{ print $1 }' file.txt. To print the name of the file awk is reading, use the built-in variable FILENAME. It prints once per line, so add FNR == 1 to print it once per file:
awk '{ print FILENAME }' /etc/hosts # the name on every line
awk 'FNR == 1 { print FILENAME }' *.conf # the name once per file
awk '{ print FILENAME ":" FNR ": " $0 }' *.conf # name, line number and line
The examples below were run with gawk, mawk and BusyBox awk on the systems listed above. Unless a section says otherwise, all three gave the same output. The two sample files are:
$ cat a.txt alice 30 bob 25 $ cat b.txt carol 41 dave 19 erin 33
1. Which awk do you have?
Debian and Ubuntu install mawk as awk. AlmaLinux, Rocky Linux and Fedora use gawk (GNU awk). Alpine Linux uses BusyBox awk. On Debian, installing the gawk package switched /usr/bin/awk from mawk to gawk. Check yours:
$ readlink -f "$(command -v awk)" /usr/bin/mawk $ awk -W version | head -1 mawk 1.3.4 20260129
On gawk, awk --version prints a line such as GNU Awk 5.3.0, API 4.0.
2. Read input from a file, several files or a pipe
awk reads every file named after the program, one line (record) at a time, and splits each line into fields $1, $2 and so on. -F sets the field separator:
awk -F: '{ print $1 }' /etc/passwd # user names
awk '/bob|dave/ { print FILENAME ": " $0 }' a.txt b.txt
The second command searches in the files and labels each match with its file name, like grep -H:
a.txt: bob 25 b.txt: dave 19
Without a file name, awk reads standard input, so cat a.txt | awk ... and awk ... < a.txt also work. In that case all three awks set FILENAME to -, because there is no file name to report.
3. Print the file name once per file with FNR
$ awk 'FNR == 1 { print FILENAME }' a.txt b.txt
a.txt
b.txt
An empty file has no line 1, so this method skips it: a.txt empty.txt b.txt printed only a.txt and b.txt. gawk has special BEGINFILE and ENDFILE rules that run even for empty files (mawk and BusyBox awk ignore them silently):
$ gawk 'ENDFILE { print FILENAME, FNR, "lines" }' a.txt empty.txt b.txt
a.txt 2 lines
empty.txt 0 lines
b.txt 3 lines
4. FNR and NR: line number per file or in total
FNR is the line number in the current file and starts again at 1 for every file. NR counts all lines read so far:
$ awk '{ print NR, FNR, FILENAME, $1 }' a.txt b.txt
1 1 a.txt alice
2 2 a.txt bob
3 1 b.txt carol
4 2 b.txt dave
5 3 b.txt erin
The difference makes a well-known trick possible: NR == FNR is only true while awk reads the first file. This prints the lines of a.txt and b.txt whose first field appears in keep.txt (which holds bob and erin):
$ awk 'NR == FNR { keep[$1]; next } $1 in keep' keep.txt a.txt b.txt
bob 25
erin 33
5. The file name in BEGIN and END
In the BEGIN block no file is open yet, so FILENAME is empty. Use ARGV[1] instead. In END, FILENAME holds the last file:
$ awk 'BEGIN { print "BEGIN: [" FILENAME "] ARGV[1]=" ARGV[1] } END { print "END: " FILENAME, NR }' a.txt b.txt
BEGIN: [] ARGV[1]=a.txt
END: b.txt 5
ARGV holds every argument after the program, including variable assignments, so ARGV[1] is not always a file (ARGV[0] is the name you called awk by, here awk):
$ awk 'BEGIN { for (i = 0; i < ARGC; i++) print i, ARGV[i] }' a.txt n=2 b.txt
0 awk
1 a.txt
2 n=2
3 b.txt
6. Skip to the next file with nextfile
nextfile stops reading the current file and moves to the next one. All three awks supported it in the test:
$ awk '{ print FILENAME; nextfile }' a.txt b.txt
a.txt
b.txt
A practical use with find: list each config file that contains a Listen line, with the line itself:
$ find conf -name '*.conf' -exec awk '/^Listen/ { print FILENAME ": " $0 }' {} +
conf/one.conf: Listen 80
conf/two.conf: Listen 8080
7. Run an awk program from a file
Longer programs are easier to keep in their own file. -f reads the program from that file:
$ cat sum.awk
{ total += $2 }
END { print "total:", total, "lines:", NR }
$ awk -f sum.awk a.txt b.txt
total: 148 lines: 5
You can also make it a script: start the file with #!/usr/bin/awk -f, run chmod +x names.awk and call it as ./names.awk a.txt.
8. Write the result to a file or edit a file in place
- Redirect the output:
awk '{ print $1 }' a.txt > names.txt. - One output file per input file, from inside awk (the brackets are needed):
awk '{ print $1 > (FILENAME ".out") }' a.txt b.txtThis createda.txt.outandb.txt.out. - Edit in place, gawk only (tested with 5.2.1 and 5.3.0). The
inplace::suffixsetting keeps the original as a backup, herea.txt.bak:gawk -i inplace -v inplace::suffix=.bak '{ $2 = $2 + 1 } 1' a.txtmawk stops withmawk: not an option: -iand BusyBox withawk: invalid option -- 'i'. With those, write to a temporary file and rename it:awk '{ $2 = $2 + 1 } 1' a.txt > a.tmp && mv a.tmp a.txt
Never redirect output to the file you are reading (awk ... a.txt > a.txt): the shell empties the file before awk starts.
9. Errors with missing files
A missing file stops all three awks before they process the files after it:
- gawk:
gawk: fatal: cannot open file `missing.txt' for reading: No such file or directory(exit code 2) - mawk:
mawk: cannot open "missing.txt" (No such file or directory)(exit code 2) - BusyBox:
awk: missing.txt: No such file or directory(exit code 1)
For text search only, grep is often simpler; for replacing text in files, see sed options and their usage.
Official documentation: GNU Awk User's Guide, built-in variables (FILENAME, FNR, NR).