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

  1. Redirect the output: awk '{ print $1 }' a.txt > names.txt.
  2. One output file per input file, from inside awk (the brackets are needed):
    awk '{ print $1 > (FILENAME ".out") }' a.txt b.txt
    This created a.txt.out and b.txt.out.
  3. Edit in place, gawk only (tested with 5.2.1 and 5.3.0). The inplace::suffix setting keeps the original as a backup, here a.txt.bak:
    gawk -i inplace -v inplace::suffix=.bak '{ $2 = $2 + 1 } 1' a.txt
    mawk stops with mawk: not an option: -i and BusyBox with awk: 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).

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Managed dedicated servers

our engineers look after the server for you, from $135 a month.

Was this answer helpful?

Still stuck? Open a ticket