<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: devTalk</title>
    <description>The latest articles on DEV Community by devTalk (@devtalk94).</description>
    <link>https://dev.to/devtalk94</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3228626%2F5495a7f0-c356-4253-865c-b83286ec1fe4.jpg</url>
      <title>DEV Community: devTalk</title>
      <link>https://dev.to/devtalk94</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devtalk94"/>
    <language>en</language>
    <item>
      <title>Laravel Context: Share Data Across Logs and Queued Jobs</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Sun, 01 Nov 2026 13:44:32 +0000</pubDate>
      <link>https://dev.to/devtalk94/laravel-context-share-data-across-logs-and-queued-jobs-336k</link>
      <guid>https://dev.to/devtalk94/laravel-context-share-data-across-logs-and-queued-jobs-336k</guid>
      <description>&lt;p&gt;You know which tenant a request belongs to in the middleware. Then the request writes a log line, dispatches a job, and the job writes more log lines on a worker with no idea who the tenant was. People usually fix this by adding &lt;code&gt;$tenantId&lt;/code&gt; to every job constructor and every &lt;code&gt;Log::info()&lt;/code&gt; call. Laravel's &lt;code&gt;Context&lt;/code&gt; facade does it for you: set the value once, and it is attached to every log entry and carried into every job you dispatch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tenant_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// in middleware, once per request&lt;/span&gt;

&lt;span class="c1"&gt;// anywhere later, including inside a queued job:&lt;/span&gt;
&lt;span class="nv"&gt;$tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tenant_id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every log entry written after that carries &lt;code&gt;tenant_id&lt;/code&gt; as metadata, and every job dispatched after that carries it to the worker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: set it in middleware
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Middleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Closure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\HttpFoundation\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddTenantContext&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tenant_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assumes your users table has a &lt;code&gt;tenant_id&lt;/code&gt; column. Use whatever identifies the tenant in your app, such as the subdomain or a route parameter. Register the middleware where the user is already available. In a Laravel 11+ app, &lt;code&gt;bootstrap/app.php&lt;/code&gt; is the place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Middleware&lt;/span&gt; &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$middleware&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;appendToGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'web'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;\App\Http\Middleware\AddTenantContext&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding it to the &lt;code&gt;web&lt;/code&gt; group rather than the global stack matters here: the session, and with it &lt;code&gt;$request-&amp;gt;user()&lt;/code&gt;, is only available after the web group's session middleware has run. If you take the tenant from the subdomain instead, ordering does not matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: read it inside the job
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Jobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Queue\ShouldQueue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Queue\Queueable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcessOrder&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ShouldQueue&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Queueable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$orderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tenant_id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 42, set in the request&lt;/span&gt;

        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Processing order'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'order_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dispatch it from a controller as usual with &lt;code&gt;ProcessOrder::dispatch($order-&amp;gt;id)&lt;/code&gt;. The job constructor does not mention the tenant at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the log looks like
&lt;/h2&gt;

&lt;p&gt;I ran this against the Laravel 13 context classes and a real database queue. The request logged a line, dispatched a job, and a separate PHP process (the "worker") started with an empty context and ran the job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order created {"order_id":7} {"tenant_id":42,"request_id":"req-abc"}      ← written in the request
context before job: []                                                    ← worker starts empty
Processing order {"order_id":7} {"tenant_id":42,"request_id":"req-abc"}   ← written in the worker
tenant_id read inside the job: 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first &lt;code&gt;{...}&lt;/code&gt; is the log context you passed to the call. The second is the shared &lt;code&gt;Context&lt;/code&gt; data, which Laravel adds as log metadata. In a default file log both appear after the message, as in this example and in the Laravel documentation. Your log formatter may place the second part differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it travels to the worker
&lt;/h2&gt;

&lt;p&gt;There is no magic beyond two hooks that Laravel registers itself, both visible in &lt;code&gt;Illuminate\Log\Context\ContextServiceProvider&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;On dispatch&lt;/strong&gt;, Laravel adds the current context to the job's payload under the key &lt;code&gt;illuminate:log:context&lt;/code&gt;. Each value is PHP-serialized.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When a worker starts a job&lt;/strong&gt;, it reads that key and loads the data back into the worker's context before your &lt;code&gt;handle()&lt;/code&gt; runs. The existing context is cleared first, so one job's data does not leak into the next job on a long-running worker.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the payload that was stored in the &lt;code&gt;jobs&lt;/code&gt; table in my test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tenant_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"i:42;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"request_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"s:7:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;req-abc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nl"&gt;"hidden"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"api_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"s:10:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;secret-xyz&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A job dispatched with an empty context has no &lt;code&gt;illuminate:log:context&lt;/code&gt; key at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hidden context
&lt;/h2&gt;

&lt;p&gt;Some data should travel with the request but stay out of the logs. Use &lt;code&gt;addHidden()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;addHidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getHidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_token'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// the token&lt;/span&gt;
&lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'api_token'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;         &lt;span class="c1"&gt;// null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hidden values are not added to log entries and are not returned by &lt;code&gt;get()&lt;/code&gt; or &lt;code&gt;all()&lt;/code&gt;. They are still carried to queued jobs. Note the payload above: &lt;code&gt;secret-xyz&lt;/code&gt; is readable in the stored job. In my test with the database queue it was plain serialized text, not encrypted. "Hidden" means hidden from logs, not protected from anyone who can read your queue storage, so do not put real secrets in context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to know
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The context is captured when you dispatch.&lt;/strong&gt; A value added after &lt;code&gt;dispatch()&lt;/code&gt; does not reach that job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Values must be serializable.&lt;/strong&gt; The context is serialized into the payload. Store IDs, strings and numbers, not closures or large objects. Eloquent models are stored by identifier, and if the record is deleted before the job runs, the value comes back as &lt;code&gt;null&lt;/code&gt;, so an ID is simpler and clearer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep it small.&lt;/strong&gt; It is added to every job payload you dispatch, so a large value is stored and transferred once per job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not replace explicit job data.&lt;/strong&gt; If a job cannot do its work without a value, such as the order it must process, pass it through the constructor. Use context for ambient data: tenant, trace ID, locale, request ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is not a security boundary.&lt;/strong&gt; Scope your queries by tenant as you do today. A shared value in a payload is data your app wrote, not proof of who may see what.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Console commands and schedulers have no request.&lt;/strong&gt; Nothing sets the tenant there. Set it explicitly where the command starts. &lt;code&gt;Context::scope()&lt;/code&gt; sets values for the duration of a callback and restores the previous state afterwards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Context work with every queue driver?
&lt;/h3&gt;

&lt;p&gt;The hook is registered with &lt;code&gt;Queue::createPayloadUsing()&lt;/code&gt; on the base &lt;code&gt;Queue&lt;/code&gt; class, and the built-in database, Redis, SQS, Beanstalkd and sync queues all extend that class, so they build their payloads the same way. I only ran the database driver.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I add a request ID or trace ID to every log line?
&lt;/h3&gt;

&lt;p&gt;Add it in the same middleware with &lt;code&gt;Context::add('trace_id', (string) Str::uuid())&lt;/code&gt;. The Laravel documentation uses exactly this pattern for a URL and trace ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between &lt;code&gt;Log::withContext()&lt;/code&gt; and &lt;code&gt;Context::add()&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Log::withContext()&lt;/code&gt; adds data to the log entries of the current process. &lt;code&gt;Context::add()&lt;/code&gt; also reaches the log entries, as metadata, and in addition travels with the jobs you dispatch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I change what is carried to jobs?
&lt;/h3&gt;

&lt;p&gt;Yes. Laravel provides &lt;code&gt;Context::dehydrating()&lt;/code&gt; and &lt;code&gt;Context::hydrated()&lt;/code&gt; callbacks. Inside them, change only the repository passed to your callback, not the &lt;code&gt;Context&lt;/code&gt; facade, as the documentation warns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://laravel.com/docs/master/context" rel="noopener noreferrer"&gt;Laravel Documentation: Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://laravel-news.com/laravel-context-facade" rel="noopener noreferrer"&gt;Laravel News: Introducing the Context Facade in Laravel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/laravel-context-logs-queued-jobs" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>context</category>
      <category>logging</category>
      <category>middleware</category>
    </item>
    <item>
      <title>AWK gsub() Examples: Find and Replace Text in Linux</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Sun, 01 Nov 2026 13:44:00 +0000</pubDate>
      <link>https://dev.to/devtalk94/awk-gsub-examples-find-and-replace-text-in-linux-2l6l</link>
      <guid>https://dev.to/devtalk94/awk-gsub-examples-find-and-replace-text-in-linux-2l6l</guid>
      <description>&lt;p&gt;&lt;code&gt;gsub()&lt;/code&gt; is AWK's find-and-replace. It replaces every match of a pattern in a line or in a single column, and it tells you how many replacements it made. It looks like &lt;code&gt;sed 's/old/new/g'&lt;/code&gt;, with one big advantage: it can work on one column and leave the rest alone. This guide covers the syntax, the special characters that trip people up, and how to use it with columns and shell variables, with the exact output of each example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/foo/, "X"); print}'&lt;/span&gt; file.txt        &lt;span class="c"&gt;# replace every foo with X on every line&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sub(/foo/, "X"); print}'&lt;/span&gt; file.txt         &lt;span class="c"&gt;# replace only the first foo on each line&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/foo/, "X", $2); print}'&lt;/span&gt; file.txt    &lt;span class="c"&gt;# replace only inside column 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If AWK is new to you, read the &lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;beginner's guide&lt;/a&gt; first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'foo bar foo\nbaz foo\nno match\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; g.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Syntax
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gsub(regex, replacement)            # works on the whole line, $0
gsub(regex, replacement, target)    # works on $2, a variable, or any other target
sub(regex, replacement, target)     # same, but replaces only the first match
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both functions change the target in place and return the number of replacements (&lt;code&gt;sub&lt;/code&gt; returns 0 or 1). The &lt;code&gt;g&lt;/code&gt; in &lt;code&gt;gsub&lt;/code&gt; stands for "global", meaning all matches.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;sub()&lt;/code&gt; versus &lt;code&gt;gsub()&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sub(/foo/, "X"); print}'&lt;/span&gt; g.txt
&lt;span class="c"&gt;# X bar foo&lt;/span&gt;
&lt;span class="c"&gt;# baz X&lt;/span&gt;
&lt;span class="c"&gt;# no match&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/foo/, "X"); print}'&lt;/span&gt; g.txt
&lt;span class="c"&gt;# X bar X&lt;/span&gt;
&lt;span class="c"&gt;# baz X&lt;/span&gt;
&lt;span class="c"&gt;# no match&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line has two &lt;code&gt;foo&lt;/code&gt;. &lt;code&gt;sub()&lt;/code&gt; changed one, &lt;code&gt;gsub()&lt;/code&gt; changed both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count the replacements
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{n = gsub(/foo/, "X"); print n}'&lt;/span&gt; g.txt
&lt;span class="c"&gt;# 2&lt;/span&gt;
&lt;span class="c"&gt;# 1&lt;/span&gt;
&lt;span class="c"&gt;# 0&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{n += gsub(/foo/, "X")} END {print n}'&lt;/span&gt; g.txt
&lt;span class="c"&gt;# 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;gsub()&lt;/code&gt; returns the count, it doubles as a way to count occurrences. There is more on that in the &lt;a href="https://dev-talk.com/awk-count-occurrences" rel="noopener noreferrer"&gt;counting occurrences guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change only some lines
&lt;/h2&gt;

&lt;p&gt;Put a pattern before the braces, or use the return value as the condition. A rule with no braces prints the line when the condition is true, so this prints only the lines that were changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/foo/ {gsub(/foo/, "X"); print}'&lt;/span&gt; g.txt     &lt;span class="c"&gt;# change and print only matching lines&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'gsub(/foo/, "X")'&lt;/span&gt; g.txt                    &lt;span class="c"&gt;# same result, shorter&lt;/span&gt;
&lt;span class="c"&gt;# X bar X&lt;/span&gt;
&lt;span class="c"&gt;# baz X&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To change matching lines but still print every line, put a lone &lt;code&gt;1&lt;/code&gt; after the replacement, which is an always-true condition that prints the line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/foo/, "X")} 1'&lt;/span&gt; g.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Replace inside one column
&lt;/h2&gt;

&lt;p&gt;Pass the field as the third argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a,foo,c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{gsub(/foo/, "X", $2); print}'&lt;/span&gt;
&lt;span class="c"&gt;# a X c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the output. The commas turned into spaces. When you change a field, AWK rebuilds the whole line from its fields and joins them with the output field separator, &lt;code&gt;OFS&lt;/code&gt;, which is a space by default. Set &lt;code&gt;OFS&lt;/code&gt; to match the input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a,foo,c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;OFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;, &lt;span class="s1"&gt;'{gsub(/foo/, "X", $2); print}'&lt;/span&gt;
&lt;span class="c"&gt;# a,X,c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same applies to space-separated input with uneven spacing. &lt;code&gt;a foo c&lt;/code&gt; comes out as &lt;code&gt;a X c&lt;/code&gt;, with single spaces, after a field is modified. This is the most common surprise with &lt;code&gt;gsub()&lt;/code&gt; on columns. For more on separators, see &lt;a href="https://dev-talk.com/awk-field-separator" rel="noopener noreferrer"&gt;AWK field separators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A practical example, converting the dashes in the first column of a CSV line to slashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'2026-10-30,x'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;OFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;, &lt;span class="s1"&gt;'{gsub(/-/, "/", $1); print}'&lt;/span&gt;
&lt;span class="c"&gt;# 2026/10/30,x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use the matched text with &lt;code&gt;&amp;amp;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In the replacement, &lt;code&gt;&amp;amp;&lt;/code&gt; stands for whatever the regex matched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'cat dog'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/[a-z]+/, "&amp;lt;&amp;amp;&amp;gt;"); print}'&lt;/span&gt;
&lt;span class="c"&gt;# &amp;lt;cat&amp;gt; &amp;lt;dog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get a literal ampersand, escape it with two backslashes inside the AWK string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'cat'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/cat/, "\\&amp;amp;"); print}'&lt;/span&gt;
&lt;span class="c"&gt;# &amp;amp;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A replacement of &lt;code&gt;a&amp;amp;b&lt;/code&gt; on the text &lt;code&gt;cat&lt;/code&gt; gives &lt;code&gt;acatb&lt;/code&gt;, not &lt;code&gt;a&amp;amp;b&lt;/code&gt;. This matters when the replacement comes from user data, such as a URL with &lt;code&gt;&amp;amp;&lt;/code&gt; in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Special characters: the dot, slashes and plus signs
&lt;/h2&gt;

&lt;p&gt;The pattern is a regular expression, so some characters mean something other than themselves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a.b.c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(".", "-"); print}'&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a.b.c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/\./, "-"); print}'&lt;/span&gt;
&lt;span class="c"&gt;# a-b-c&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a.b.c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub("\\.", "-"); print}'&lt;/span&gt;
&lt;span class="c"&gt;# a-b-c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unescaped &lt;code&gt;.&lt;/code&gt; matches any character, so the first command replaced all five characters. Escape it with a backslash in a regex literal (&lt;code&gt;/\./&lt;/code&gt;) or with two backslashes in a string (&lt;code&gt;"\\."&lt;/code&gt;). A character class also works: &lt;code&gt;[.]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A slash in a regex literal must be escaped, or you can use a string instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'/usr/local/bin'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/\//, "_"); print}'&lt;/span&gt;     &lt;span class="c"&gt;# _usr_local_bin&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'/usr/local/bin'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub("/", "_"); print}'&lt;/span&gt;      &lt;span class="c"&gt;# _usr_local_bin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other characters that need escaping or brackets to be treated literally include &lt;code&gt;+ * ? ( ) [ ] ^ $ |&lt;/code&gt; and the backslash itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common recipes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# delete characters&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a1b22c333'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/[0-9]/, ""); print}'&lt;/span&gt;                 &lt;span class="c"&gt;# abc&lt;/span&gt;

&lt;span class="c"&gt;# trim spaces from both ends&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'   hi there   '&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/^ +| +$/, ""); print "[" $0 "]"}'&lt;/span&gt;   &lt;span class="c"&gt;# [hi there]&lt;/span&gt;

&lt;span class="c"&gt;# squeeze repeated spaces into one&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a    b     c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/ +/, " "); print}'&lt;/span&gt;                 &lt;span class="c"&gt;# a b c&lt;/span&gt;

&lt;span class="c"&gt;# tabs to commas&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a\tb\tc\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/\t/, ","); print}'&lt;/span&gt;                  &lt;span class="c"&gt;# a,b,c&lt;/span&gt;

&lt;span class="c"&gt;# replace every run of digits&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'order 123 and 45'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/[0-9]+/, "#"); print}'&lt;/span&gt;         &lt;span class="c"&gt;# order # and #&lt;/span&gt;

&lt;span class="c"&gt;# strip punctuation&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Hi, there! 99'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/[[:punct:]]/, ""); print}'&lt;/span&gt;        &lt;span class="c"&gt;# Hi there 99&lt;/span&gt;

&lt;span class="c"&gt;# remove Windows line endings&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'ab\r\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sub(/\r$/, ""); print}'&lt;/span&gt;                      &lt;span class="c"&gt;# ab&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anchors work as in other regex tools. On &lt;code&gt;foo foo&lt;/code&gt;, &lt;code&gt;sub(/^foo/, "X")&lt;/code&gt; gives &lt;code&gt;X foo&lt;/code&gt; and &lt;code&gt;sub(/foo$/, "X")&lt;/code&gt; gives &lt;code&gt;foo X&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use shell variables with gsub
&lt;/h2&gt;

&lt;p&gt;Pass the value with &lt;code&gt;-v&lt;/code&gt; and use it as the replacement or as the pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NEW &lt;span class="s1"&gt;'{gsub(/foo/, r); print}'&lt;/span&gt; g.txt
&lt;span class="c"&gt;# NEW bar NEW&lt;/span&gt;
&lt;span class="c"&gt;# baz NEW&lt;/span&gt;
&lt;span class="c"&gt;# no match&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foo &lt;span class="s1"&gt;'{gsub(p, "X"); print}'&lt;/span&gt; g.txt
&lt;span class="c"&gt;# X bar X&lt;/span&gt;
&lt;span class="c"&gt;# baz X&lt;/span&gt;
&lt;span class="c"&gt;# no match&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things to watch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A pattern in a variable is still a regex.&lt;/strong&gt; With &lt;code&gt;-v p='a+b'&lt;/code&gt;, the text &lt;code&gt;a+b&lt;/code&gt; was not replaced, because the pattern means "one or more a, then b". With &lt;code&gt;-v p='a[+]b'&lt;/code&gt; it was replaced. Escape special characters in the variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An&lt;/strong&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;strong&gt;in the variable is special again.&lt;/strong&gt; With &lt;code&gt;-v r='A&amp;amp;B'&lt;/code&gt;, &lt;code&gt;foo&lt;/code&gt; became &lt;code&gt;AfooB&lt;/code&gt;. To get a literal &lt;code&gt;&amp;amp;&lt;/code&gt;, pass &lt;code&gt;-v r='A\\&amp;amp;B'&lt;/code&gt;; the result was &lt;code&gt;A&amp;amp;B&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt;&lt;strong&gt;processes backslash escapes.&lt;/strong&gt; &lt;code&gt;-v r='a\tb'&lt;/code&gt; puts a real tab in the variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Case-insensitive replacement
&lt;/h2&gt;

&lt;p&gt;AWK regexes are case sensitive. The portable approach is to work on a lowercased copy, which changes the case of the whole line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Foo FOO foo'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{t = tolower($0); n = gsub(/foo/, "X", t); print n, t}'&lt;/span&gt;
&lt;span class="c"&gt;# 3 X X X&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This counts all three variants. If you need to keep the rest of the line's original case, use gawk and its &lt;code&gt;IGNORECASE&lt;/code&gt; variable. mawk silently ignores that variable, so the output would be wrong without any error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capture groups: &lt;code&gt;gensub()&lt;/code&gt; in gawk only
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sub()&lt;/code&gt; and &lt;code&gt;gsub()&lt;/code&gt; cannot reuse parts of the match, as &lt;code&gt;\1&lt;/code&gt; does in sed. gawk adds &lt;code&gt;gensub()&lt;/code&gt; for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'john smith'&lt;/span&gt; | gawk &lt;span class="s1"&gt;'{print gensub(/(\w+) (\w+)/, "\\2, \\1", "g")}'&lt;/span&gt;
&lt;span class="c"&gt;# smith, john&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On mawk, the same function fails with &lt;code&gt;function gensub never defined&lt;/code&gt;. For portable code, split the line into fields instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'john smith'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2 ", " $1}'&lt;/span&gt;
&lt;span class="c"&gt;# smith, john&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To pull a piece out of a line rather than replace it, &lt;code&gt;match()&lt;/code&gt; with &lt;code&gt;substr()&lt;/code&gt; works everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'id=42;x'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{if (match($0, /[0-9]+/)) print substr($0, RSTART, RLENGTH)}'&lt;/span&gt;
&lt;span class="c"&gt;# 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Change the file itself
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gsub()&lt;/code&gt; only changes the text AWK holds in memory. The file on disk stays the same until you save the output. Write to a new file and move it into place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{gsub(/foo/, "X")} 1'&lt;/span&gt; g.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; g.tmp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;g.tmp g.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On gawk you can edit in place. I ran this on gawk 5.2.1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gawk &lt;span class="nt"&gt;-i&lt;/span&gt; inplace &lt;span class="s1"&gt;'{gsub(/foo/, "X")} 1'&lt;/span&gt; g.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never redirect to the file you are reading (&lt;code&gt;awk ... g.txt &amp;gt; g.txt&lt;/code&gt;), because the shell empties it first.&lt;/p&gt;

&lt;h2&gt;
  
  
  gsub or sed?
&lt;/h2&gt;

&lt;p&gt;For a plain global replacement on whole lines, &lt;code&gt;sed 's/foo/X/g' file&lt;/code&gt; is shorter and gives the same result. Choose AWK when you need to change only one column, replace only on lines that meet a condition on another column, count replacements, or combine the replacement with other calculations. Capture groups are easier in sed (&lt;code&gt;\1&lt;/code&gt;), or in gawk with &lt;code&gt;gensub()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every character was replaced.&lt;/strong&gt; You used &lt;code&gt;"."&lt;/code&gt; or &lt;code&gt;/./&lt;/code&gt;. Escape the dot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commas or spacing changed after editing a column.&lt;/strong&gt; Set &lt;code&gt;OFS&lt;/code&gt; to the same value as &lt;code&gt;-F&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The file did not change.&lt;/strong&gt; &lt;code&gt;gsub()&lt;/code&gt; only affects the output. Save it with a new file or &lt;code&gt;-i inplace&lt;/code&gt; on gawk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing matched with a variable pattern.&lt;/strong&gt; The variable holds a regex. Escape &lt;code&gt;+ ? . ( ) [ ] ^ $ |&lt;/code&gt; or use a bracket class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unexpected&lt;/strong&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;strong&gt;in the output.&lt;/strong&gt; It is replaced by the matched text. Escape it as &lt;code&gt;\\&amp;amp;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whole line changed after modifying&lt;/strong&gt;&lt;code&gt;$0&lt;/code&gt;&lt;strong&gt;.&lt;/strong&gt; If you change &lt;code&gt;$0&lt;/code&gt; with &lt;code&gt;gsub()&lt;/code&gt;, AWK splits it into fields again. After &lt;code&gt;gsub(/ /, ",")&lt;/code&gt; on &lt;code&gt;a b c&lt;/code&gt;, &lt;code&gt;NF&lt;/code&gt; was 1 and &lt;code&gt;$1&lt;/code&gt; was &lt;code&gt;a,b,c&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the difference between sub and gsub in AWK?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sub()&lt;/code&gt; replaces the first match in the target. &lt;code&gt;gsub()&lt;/code&gt; replaces every match.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I replace text in one column only?
&lt;/h3&gt;

&lt;p&gt;Give the column as the third argument: &lt;code&gt;gsub(/old/, "new", $3)&lt;/code&gt;. Set &lt;code&gt;OFS&lt;/code&gt; to your delimiter so the line is rebuilt correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I use a variable in gsub?
&lt;/h3&gt;

&lt;p&gt;Pass it with &lt;code&gt;-v&lt;/code&gt;, for example &lt;code&gt;awk -v r=NEW '{gsub(/foo/, r)} 1' file&lt;/code&gt;. The pattern and the replacement can both be variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does AWK gsub support regex groups like \1?
&lt;/h3&gt;

&lt;p&gt;No. Standard AWK has no backreferences in the replacement. gawk provides &lt;code&gt;gensub()&lt;/code&gt;, which does, but it is not available in mawk.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I replace a literal dot or slash?
&lt;/h3&gt;

&lt;p&gt;Escape it: &lt;code&gt;/\./&lt;/code&gt; and &lt;code&gt;/\//&lt;/code&gt; in a regex literal, or &lt;code&gt;"\\."&lt;/code&gt; in a string. A bracket such as &lt;code&gt;[.]&lt;/code&gt; also works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next in this series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;AWK Command in Linux: A Beginner's Guide with Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-print-column" rel="noopener noreferrer"&gt;How to Print a Column with AWK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-remove-duplicates" rel="noopener noreferrer"&gt;AWK Remove Duplicates: Delete Duplicate Lines, Columns and CSV Rows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-cheat-sheet" rel="noopener noreferrer"&gt;AWK Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/awk-gsub" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>awk</category>
      <category>linux</category>
      <category>shell</category>
      <category>gsub</category>
    </item>
    <item>
      <title>AWK Remove Duplicates: Lines, Columns and CSV Rows</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Sun, 01 Nov 2026 02:27:21 +0000</pubDate>
      <link>https://dev.to/devtalk94/awk-remove-duplicates-lines-columns-and-csv-rows-27p8</link>
      <guid>https://dev.to/devtalk94/awk-remove-duplicates-lines-columns-and-csv-rows-27p8</guid>
      <description>&lt;p&gt;The shortest way to remove duplicate lines from a file in Linux is an AWK one-liner that most people copy without ever reading. It is worth understanding, because the same trick handles duplicates by column, duplicate CSV rows, keeping the last occurrence instead of the first, and listing only the lines that repeat. This guide covers each case with sample input and the exact output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'!seen[$0]++'&lt;/span&gt; file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prints each distinct line the first time it appears and drops every later copy. Unlike &lt;code&gt;sort -u&lt;/code&gt;, it keeps the original order of the lines and does not need the file to be sorted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample files
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'apple\nbanana\napple\ncherry\nbanana\napple\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dup.txt

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; emails.csv &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
id,email,city
1,ann@x.com,Colombo
2,bob@x.com,Kandy
3,ann@x.com,Galle
4,cy@x.com,Kandy
5,bob@x.com,Colombo
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# used further down&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a\na\nb\na\nc\nc\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; con.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Apple\napple\nAPPLE\nbanana\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'apple\napple \nbanana\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sp.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'x\r\nx\nx\r\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; crlf.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Remove duplicate lines and keep the order
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'!seen[$0]++'&lt;/span&gt; dup.txt
&lt;span class="c"&gt;# apple&lt;/span&gt;
&lt;span class="c"&gt;# banana&lt;/span&gt;
&lt;span class="c"&gt;# cherry&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sort -u dup.txt&lt;/code&gt; gives the same three lines here, but only because they happen to be in alphabetical order already. &lt;code&gt;sort -u&lt;/code&gt; always returns sorted output; the AWK version returns the lines in the order they first appeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  How &lt;code&gt;!seen[$0]++&lt;/code&gt; works
&lt;/h2&gt;

&lt;p&gt;Read it from the inside out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;$0&lt;/code&gt; is the whole current line, used as a key in the array &lt;code&gt;seen&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seen[$0]++&lt;/code&gt; adds one to that entry, but the value of the expression is the number &lt;em&gt;before&lt;/em&gt; the increment. A key that has never been used counts as 0.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!&lt;/code&gt; turns that into true when it was 0 (first time) and false for 1, 2, 3... (a repeat).&lt;/li&gt;
&lt;li&gt;An AWK rule with a condition and no braces prints the line when the condition is true.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the first &lt;code&gt;apple&lt;/code&gt; sees 0, passes, and is printed. The second sees 1 and is skipped. The condition is true only for the first copy of each line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove duplicates by one column
&lt;/h2&gt;

&lt;p&gt;Use the column as the key instead of the whole line. This keeps the first row for each email address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'!seen[$2]++'&lt;/span&gt; emails.csv
&lt;span class="c"&gt;# id,email,city&lt;/span&gt;
&lt;span class="c"&gt;# 1,ann@x.com,Colombo&lt;/span&gt;
&lt;span class="c"&gt;# 2,bob@x.com,Kandy&lt;/span&gt;
&lt;span class="c"&gt;# 4,cy@x.com,Kandy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rows 3 and 5 are gone because their emails had already appeared. &lt;code&gt;-F,&lt;/code&gt; sets the comma as the field separator (see &lt;a href="https://dev-talk.com/awk-field-separator" rel="noopener noreferrer"&gt;AWK field separators&lt;/a&gt;). The header row was kept only because no earlier line had the key &lt;code&gt;email&lt;/code&gt;. If a data row could ever match the header text, protect the header explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR==1 || !seen[$2]++'&lt;/span&gt; emails.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Remove duplicates by several columns
&lt;/h2&gt;

&lt;p&gt;Join the columns with a comma inside the brackets. AWK builds one key from them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a,1\na,1\na,2\nb,1\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'!seen[$1,$2]++'&lt;/span&gt;
&lt;span class="c"&gt;# a,1&lt;/span&gt;
&lt;span class="c"&gt;# a,2&lt;/span&gt;
&lt;span class="c"&gt;# b,1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two rows count as duplicates only when both columns match. &lt;code&gt;a,1&lt;/code&gt; and &lt;code&gt;a,2&lt;/code&gt; share the first column but are different rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the last occurrence instead of the first
&lt;/h2&gt;

&lt;p&gt;Reverse the file, keep the first of each, and reverse it back. &lt;code&gt;tac&lt;/code&gt; prints a file from the last line to the first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tac &lt;/span&gt;emails.csv | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'!seen[$2]++'&lt;/span&gt; | &lt;span class="nb"&gt;tac&lt;/span&gt;
&lt;span class="c"&gt;# id,email,city&lt;/span&gt;
&lt;span class="c"&gt;# 3,ann@x.com,Galle&lt;/span&gt;
&lt;span class="c"&gt;# 4,cy@x.com,Kandy&lt;/span&gt;
&lt;span class="c"&gt;# 5,bob@x.com,Colombo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each email now appears once, with its last row. The result is ordered by where each row last appeared, not by where it first appeared. &lt;code&gt;tac&lt;/code&gt; is part of GNU coreutils; on macOS use &lt;code&gt;tail -r&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove only consecutive duplicates
&lt;/h2&gt;

&lt;p&gt;This is what &lt;code&gt;uniq&lt;/code&gt; does. Compare each line with the previous one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$0 != prev {print} {prev = $0}'&lt;/span&gt; con.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a file with the lines &lt;code&gt;a a b a c c&lt;/code&gt;, this prints &lt;code&gt;a b a c&lt;/code&gt;. The second &lt;code&gt;a&lt;/code&gt; after &lt;code&gt;b&lt;/code&gt; stays, because it is not next to another &lt;code&gt;a&lt;/code&gt;. Plain &lt;code&gt;uniq&lt;/code&gt; gave the same result. Unlike &lt;code&gt;!seen[$0]++&lt;/code&gt;, this uses almost no memory because it only remembers one line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show only the duplicates
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'seen[$0]++'&lt;/span&gt; dup.txt             &lt;span class="c"&gt;# every repeated copy (2nd, 3rd, ...)&lt;/span&gt;
&lt;span class="c"&gt;# apple&lt;/span&gt;
&lt;span class="c"&gt;# banana&lt;/span&gt;
&lt;span class="c"&gt;# apple&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'seen[$0]++ == 1'&lt;/span&gt; dup.txt        &lt;span class="c"&gt;# each duplicated value, once&lt;/span&gt;
&lt;span class="c"&gt;# apple&lt;/span&gt;
&lt;span class="c"&gt;# banana&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the &lt;code&gt;!&lt;/code&gt;, the condition is true for the second and later copies. Adding &lt;code&gt;== 1&lt;/code&gt; selects only the second copy, so each duplicated value is printed once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Show lines that appear only once
&lt;/h3&gt;

&lt;p&gt;This needs two passes, so name the file twice. The first pass counts, the second prints the lines whose count is 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR==FNR {c[$0]++; next} c[$0] == 1'&lt;/span&gt; dup.txt dup.txt
&lt;span class="c"&gt;# cherry&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;NR==FNR&lt;/code&gt; is true only while AWK reads the first copy of the file. &lt;code&gt;next&lt;/code&gt; skips to the following line without running the second rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  Count how many times each line appears
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{c[$0]++} END {for (k in c) print c[k], k}'&lt;/span&gt; dup.txt | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k1&lt;/span&gt;,1nr &lt;span class="nt"&gt;-k2&lt;/span&gt;
&lt;span class="c"&gt;# 3 apple&lt;/span&gt;
&lt;span class="c"&gt;# 2 banana&lt;/span&gt;
&lt;span class="c"&gt;# 1 cherry&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same technique as in &lt;a href="https://dev-talk.com/awk-count-occurrences" rel="noopener noreferrer"&gt;counting occurrences with AWK&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignore case or stray whitespace
&lt;/h2&gt;

&lt;p&gt;Two lines that look the same to you are different keys to AWK if their case, trailing spaces or line endings differ. Normalise the key, and keep the original line for printing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'!seen[tolower($0)]++'&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;.txt          &lt;span class="c"&gt;# Apple, apple, APPLE, banana  -&amp;gt;  Apple, banana&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sub(/[ \t]+$/, "")} !seen[$0]++'&lt;/span&gt; sp.txt   &lt;span class="c"&gt;# "apple" and "apple " become one line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first command the key is lowercase, but the line printed is the original one, so the first spelling (&lt;code&gt;Apple&lt;/code&gt;) is kept. In the second, &lt;code&gt;sub()&lt;/code&gt; strips trailing spaces from the line before the duplicate check, and the cleaned line is what gets printed.&lt;/p&gt;

&lt;p&gt;A file from Windows has a hidden carriage return at the end of each line. A test file with &lt;code&gt;x\r\n&lt;/code&gt;, &lt;code&gt;x\n&lt;/code&gt; and &lt;code&gt;x\r\n&lt;/code&gt; produced 2 lines with the plain command and 1 after removing the carriage return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sub(/\r$/, "")} !seen[$0]++'&lt;/span&gt; crlf.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Remove duplicates across several files
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'!seen[$0]++'&lt;/span&gt; file1.txt file2.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; merged.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;seen&lt;/code&gt; array is shared across all the files in one run, so a line from the second file that already appeared in the first is dropped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save the result
&lt;/h2&gt;

&lt;p&gt;Never redirect output into the file you are reading, as in &lt;code&gt;awk '...' file &amp;gt; file&lt;/code&gt;. The shell empties the file before AWK starts, and you lose the data. Write to a new file and move it into place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'!seen[$0]++'&lt;/span&gt; dup.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dup.clean &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;dup.clean dup.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With gawk you can edit in place instead. I ran this on gawk 5.2.1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gawk &lt;span class="nt"&gt;-i&lt;/span&gt; inplace &lt;span class="s1"&gt;'!seen[$0]++'&lt;/span&gt; dup.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-i inplace&lt;/code&gt; is a gawk feature. mawk does not have it, so use the temporary file method there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count how many lines were removed
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'!seen[$0]++ {kept++} END {print NR - kept, "removed"}'&lt;/span&gt; dup.txt
&lt;span class="c"&gt;# 3 removed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  AWK, sort -u or uniq?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Keeps original order&lt;/th&gt;
&lt;th&gt;Needs sorted input&lt;/th&gt;
&lt;th&gt;Memory&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk '!seen[$0]++'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Stores every distinct line&lt;/td&gt;
&lt;td&gt;Order matters, or dedupe by column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sort -u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No (sorted output)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Spills to disk when large&lt;/td&gt;
&lt;td&gt;Very large files, sorted output is fine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uniq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (only removes adjacent copies)&lt;/td&gt;
&lt;td&gt;One line&lt;/td&gt;
&lt;td&gt;Already sorted input&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The AWK array holds every distinct line in memory. For a file with millions of mostly unique lines that can be large, and &lt;code&gt;sort -u&lt;/code&gt; is the safer choice. For the usual log, list or CSV, AWK is fast and simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Duplicates" that are not removed.&lt;/strong&gt; Trailing spaces, different case or Windows line endings make lines differ. Normalise the key as shown above. &lt;code&gt;cat -A file&lt;/code&gt; shows hidden characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The header disappeared or was duplicated.&lt;/strong&gt; Use &lt;code&gt;NR==1 || !seen[$2]++&lt;/code&gt; to always keep it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quoted CSV fields.&lt;/strong&gt; &lt;code&gt;-F,&lt;/code&gt; splits inside quotes, so a value like &lt;code&gt;"Smith, John"&lt;/code&gt; becomes two fields and the wrong key is used. For quoted CSV, deduplicate on a column that has no commas, or use a CSV-aware tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The file came out empty.&lt;/strong&gt; You redirected into the input file. Write to a new file first.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I remove duplicate lines in Linux without sorting?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;awk '!seen[$0]++' file&lt;/code&gt;. It keeps the first copy of each line and preserves the original order.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I remove duplicates based on one column in AWK?
&lt;/h3&gt;

&lt;p&gt;Use that column as the array key, for example &lt;code&gt;awk -F, '!seen[$2]++' file.csv&lt;/code&gt; to keep the first row for each value in column 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I keep the last duplicate instead of the first?
&lt;/h3&gt;

&lt;p&gt;Reverse the file, deduplicate, and reverse again: &lt;code&gt;tac file | awk '!seen[$0]++' | tac&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does &lt;code&gt;!seen[$0]++&lt;/code&gt; mean?
&lt;/h3&gt;

&lt;p&gt;It is true only the first time a line is seen. &lt;code&gt;seen[$0]++&lt;/code&gt; returns the previous count, and the &lt;code&gt;!&lt;/code&gt; turns a count of 0 into true, so the default action (print) runs for first occurrences only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the AWK method case sensitive?
&lt;/h3&gt;

&lt;p&gt;Yes. Use &lt;code&gt;!seen[tolower($0)]++&lt;/code&gt; to treat &lt;code&gt;Apple&lt;/code&gt; and &lt;code&gt;apple&lt;/code&gt; as the same line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next in this series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;AWK Command in Linux: A Beginner's Guide with Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-count-occurrences" rel="noopener noreferrer"&gt;AWK Count Occurrences: Lines, Words, Characters and Values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-sum-column" rel="noopener noreferrer"&gt;How to Sum a Column with AWK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev-talk.com/awk-cheat-sheet" rel="noopener noreferrer"&gt;AWK Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/awk-remove-duplicates" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>awk</category>
      <category>linux</category>
      <category>shell</category>
    </item>
    <item>
      <title>AWK Count Occurrences: Lines, Words, Characters &amp; Values</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Sat, 31 Oct 2026 05:19:30 +0000</pubDate>
      <link>https://dev.to/devtalk94/awk-count-occurrences-lines-words-characters-values-4j1i</link>
      <guid>https://dev.to/devtalk94/awk-count-occurrences-lines-words-characters-values-4j1i</guid>
      <description>&lt;p&gt;"How many times does this appear?" can mean four different things: how many lines contain it, how many times it appears in total, how many times each value shows up in a column, or how many distinct values there are. AWK handles all four with a few short commands. This guide shows each one with sample input and the exact output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/error/ {c++} END {print c+0}'&lt;/span&gt; file.txt                 &lt;span class="c"&gt;# lines containing "error"&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{n += gsub(/error/, "&amp;amp;")} END {print n+0}'&lt;/span&gt; file.txt     &lt;span class="c"&gt;# every occurrence of "error"&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{c[$1]++} END {for (k in c) print c[k], k}'&lt;/span&gt; file.txt    &lt;span class="c"&gt;# how often each value of column 1 appears&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first counts lines, the second counts every occurrence even when one line has several, and the third counts per value. If AWK itself is new to you, start with the &lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;beginner's guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample files
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; words.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
error: disk full
ok
Error: timeout, error again
warning: low memory
error error error
ERROR: failed
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sales.csv &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
date,region,product,qty,price
2026-10-01,north,pen,10,1.50
2026-10-01,south,book,3,12.00
2026-10-02,north,book,2,12.00
2026-10-02,east,pen,25,1.50
2026-10-03,south,pen,5,1.50
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; access.log &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
10.0.0.1 - - [24/Oct/2026:10:00:01 +0000] "GET /index.php HTTP/1.1" 200 512
10.0.0.2 - - [24/Oct/2026:10:00:02 +0000] "GET /about HTTP/1.1" 404 128
10.0.0.1 - - [24/Oct/2026:10:00:03 +0000] "POST /login HTTP/1.1" 302 0
10.0.0.3 - - [24/Oct/2026:10:00:04 +0000] "GET /index.php HTTP/1.1" 200 512
10.0.0.2 - - [24/Oct/2026:10:00:05 +0000] "GET /missing HTTP/1.1" 404 128
10.0.0.1 - - [24/Oct/2026:10:00:06 +0000] "GET /index.php HTTP/1.1" 500 64
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Count lines that match
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/error/ {c++} END {print c+0}'&lt;/span&gt; words.txt
&lt;span class="c"&gt;# 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each matching line adds one to &lt;code&gt;c&lt;/code&gt;, and the &lt;code&gt;END&lt;/code&gt; block prints the total. The &lt;code&gt;+0&lt;/code&gt; matters. If nothing matches, &lt;code&gt;c&lt;/code&gt; was never set and plain &lt;code&gt;print c&lt;/code&gt; prints an empty line instead of a number. With &lt;code&gt;c+0&lt;/code&gt; you get &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Only lowercase &lt;code&gt;error&lt;/code&gt; matched here, so the lines with &lt;code&gt;Error&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt; were not counted. The case-insensitive version is below.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;grep -c error words.txt&lt;/code&gt; gives the same 3. AWK earns its place when the count needs a condition on a column. In the sample web server log, the status code is the second to last field, so the number of error responses (status 400 or higher) is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$(NF-1) &amp;gt;= 400 {c++} END {print c+0}'&lt;/span&gt; access.log
&lt;span class="c"&gt;# 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;NF&lt;/code&gt; is the number of fields, so &lt;code&gt;$(NF-1)&lt;/code&gt; is the one before the last. The parentheses are required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count every occurrence, not just lines
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;grep -c&lt;/code&gt; and the first command above count &lt;em&gt;lines&lt;/em&gt;. The line &lt;code&gt;error error error&lt;/code&gt; counts once. To count every occurrence, use the return value of &lt;code&gt;gsub()&lt;/code&gt;, which is the number of replacements it made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{t = $0; n += gsub(/error/, "&amp;amp;", t)} END {print n}'&lt;/span&gt; words.txt
&lt;span class="c"&gt;# 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replacing a match with &lt;code&gt;&amp;amp;&lt;/code&gt; means "put back the text that matched", so nothing changes, but you still get the count. Working on a copy (&lt;code&gt;t&lt;/code&gt;) leaves the original line untouched. The same count from the shell is &lt;code&gt;grep -o error words.txt | wc -l&lt;/code&gt;, which also gives 5.&lt;/p&gt;

&lt;p&gt;To see the count per line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{n = gsub(/error/, "&amp;amp;"); print NR": "n}'&lt;/span&gt; words.txt
&lt;span class="c"&gt;# 1: 1&lt;/span&gt;
&lt;span class="c"&gt;# 2: 0&lt;/span&gt;
&lt;span class="c"&gt;# 3: 1&lt;/span&gt;
&lt;span class="c"&gt;# 4: 0&lt;/span&gt;
&lt;span class="c"&gt;# 5: 3&lt;/span&gt;
&lt;span class="c"&gt;# 6: 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ignore case
&lt;/h3&gt;

&lt;p&gt;Lowercase a copy of the line first. This works in every awk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{t = tolower($0); n += gsub(/error/, "&amp;amp;", t)} END {print n}'&lt;/span&gt; words.txt
&lt;span class="c"&gt;# 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gawk also has an &lt;code&gt;IGNORECASE&lt;/code&gt; variable, but mawk silently ignores it, so &lt;code&gt;tolower()&lt;/code&gt; is the safer habit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Whole words only
&lt;/h3&gt;

&lt;p&gt;A regex match also finds &lt;code&gt;error&lt;/code&gt; inside &lt;code&gt;errors&lt;/code&gt; or &lt;code&gt;error:&lt;/code&gt;. To count only fields that are exactly the word, compare the fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{for (i = 1; i &amp;lt;= NF; i++) if ($i == "error") n++} END {print n+0}'&lt;/span&gt; words.txt
&lt;span class="c"&gt;# 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives 4, not 5, because &lt;code&gt;error:&lt;/code&gt; on the first line has a colon attached and is not equal to &lt;code&gt;error&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Count one character
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'banana'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print gsub(/a/, "&amp;amp;")}'&lt;/span&gt;
&lt;span class="c"&gt;# 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two traps. A quoted string is a regular expression, so &lt;code&gt;gsub(".", ...)&lt;/code&gt; matches every character; on &lt;code&gt;a.b.c&lt;/code&gt; it returned 5, while &lt;code&gt;gsub(/\./, ...)&lt;/code&gt; returned 2. And matches do not overlap: &lt;code&gt;gsub(/aa/, ...)&lt;/code&gt; on &lt;code&gt;aaaa&lt;/code&gt; returned 2, not 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count how often each value appears
&lt;/h2&gt;

&lt;p&gt;This is the most useful pattern. Use the value as an array key and add one every time you see it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {c[$2]++} END {for (k in c) print k, c[k]}'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;span class="c"&gt;# east 1&lt;/span&gt;
&lt;span class="c"&gt;# north 2&lt;/span&gt;
&lt;span class="c"&gt;# south 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;c[$2]++&lt;/code&gt; reads as "increase the counter for this region". A key that has not been seen yet starts at zero, so there is no setup. &lt;code&gt;NR&amp;gt;1&lt;/code&gt; skips the header, and &lt;code&gt;-F,&lt;/code&gt; sets the comma as the separator (see &lt;a href="https://dev-talk.com/awk-field-separator" rel="noopener noreferrer"&gt;AWK field separators&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The order of &lt;code&gt;for (k in c)&lt;/code&gt; is not defined and differs between awks, so pipe the result to &lt;code&gt;sort&lt;/code&gt; as above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sort by count, highest first
&lt;/h3&gt;

&lt;p&gt;Print the count first, then sort numerically in reverse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {c[$3]++} END {for (k in c) print c[k], k}'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k1&lt;/span&gt;,1nr &lt;span class="nt"&gt;-k2&lt;/span&gt;
&lt;span class="c"&gt;# 3 pen&lt;/span&gt;
&lt;span class="c"&gt;# 2 book&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second sort key, &lt;code&gt;-k2&lt;/code&gt;, puts names with equal counts in a stable order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Top visitors in a web server log
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{c[$1]++} END {for (k in c) print c[k], k}'&lt;/span&gt; access.log | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k1&lt;/span&gt;,1nr &lt;span class="nt"&gt;-k2&lt;/span&gt;
&lt;span class="c"&gt;# 3 10.0.0.1&lt;/span&gt;
&lt;span class="c"&gt;# 2 10.0.0.2&lt;/span&gt;
&lt;span class="c"&gt;# 1 10.0.0.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;| head -10&lt;/code&gt; for a top 10. Change &lt;code&gt;$1&lt;/code&gt; to &lt;code&gt;$7&lt;/code&gt; to count requested paths instead of IP addresses, or to &lt;code&gt;$(NF-1)&lt;/code&gt; to count status codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{c[$(NF-1)]++} END {for (k in c) print k, c[k]}'&lt;/span&gt; access.log | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;span class="c"&gt;# 200 2&lt;/span&gt;
&lt;span class="c"&gt;# 302 1&lt;/span&gt;
&lt;span class="c"&gt;# 404 2&lt;/span&gt;
&lt;span class="c"&gt;# 500 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The column numbers assume the common access log layout shown in the sample, where the quoted request is split by spaces into method, path and protocol. If your log format differs, check with &lt;code&gt;awk '{print $7}' access.log | head&lt;/code&gt; first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add percentages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {c[$2]++; t++} END {for (k in c) printf "%-6s %d %.0f%%\n", k, c[k], c[k]/t*100}'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;span class="c"&gt;# east   1 20%&lt;/span&gt;
&lt;span class="c"&gt;# north  2 40%&lt;/span&gt;
&lt;span class="c"&gt;# south  2 40%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Count distinct values
&lt;/h2&gt;

&lt;p&gt;To know how many different regions exist, not how often each appears:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 &amp;amp;&amp;amp; !seen[$2]++ {n++} END {print n}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 3&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {s[$2]} END {print length(s)}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first counts a value the first time it is seen. The second only touches each key, then asks for the size of the array. &lt;code&gt;length(array)&lt;/code&gt; works in gawk and mawk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count lines and words
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'END {print NR}'&lt;/span&gt; words.txt          &lt;span class="c"&gt;# 6   (lines)&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{w += NF} END {print w}'&lt;/span&gt; words.txt &lt;span class="c"&gt;# 16  (words)&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'!NF {c++} END {print c+0}'&lt;/span&gt; file    &lt;span class="c"&gt;# blank lines&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These match &lt;code&gt;wc -l&lt;/code&gt; and &lt;code&gt;wc -w&lt;/code&gt;. &lt;code&gt;NR&lt;/code&gt; is the line number of the last line read, and &lt;code&gt;NF&lt;/code&gt; is the field count of each line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count words across a file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{for (i = 1; i &amp;lt;= NF; i++) {w = tolower($i); gsub(/[^a-z]/, "", w); if (w != "") f[w]++}}
     END {for (k in f) print f[k], k}'&lt;/span&gt; words.txt | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k1&lt;/span&gt;,1nr &lt;span class="nt"&gt;-k2&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-4&lt;/span&gt;
&lt;span class="c"&gt;# 7 error&lt;/span&gt;
&lt;span class="c"&gt;# 1 again&lt;/span&gt;
&lt;span class="c"&gt;# 1 disk&lt;/span&gt;
&lt;span class="c"&gt;# 1 failed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each word, lowercase it, strip everything that is not a letter so &lt;code&gt;error:&lt;/code&gt; and &lt;code&gt;Error&lt;/code&gt; both become &lt;code&gt;error&lt;/code&gt;, and count it. Because of the &lt;code&gt;[^a-z]&lt;/code&gt; filter this removes digits and non-English letters too, so adjust the pattern for your text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a shell variable as the search term
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;error &lt;span class="s1"&gt;'$0 ~ w {c++} END {print c+0}'&lt;/span&gt; words.txt
&lt;span class="c"&gt;# 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;~&lt;/code&gt; the variable is used as a regular expression, so special characters such as &lt;code&gt;.&lt;/code&gt; keep their regex meaning. Do not put the shell variable inside the program text; pass it with &lt;code&gt;-v&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A blank line instead of 0.&lt;/strong&gt; You printed a counter that was never set. Print &lt;code&gt;c+0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The count is lower than expected.&lt;/strong&gt; Counting lines (&lt;code&gt;c++&lt;/code&gt; on a match) and counting occurrences (&lt;code&gt;gsub&lt;/code&gt;) give different totals when a line has the term more than once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Header row counted.&lt;/strong&gt; Add &lt;code&gt;NR&amp;gt;1&lt;/code&gt; to the rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Case variants counted separately.&lt;/strong&gt; &lt;code&gt;Error&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; and &lt;code&gt;ERROR&lt;/code&gt; are different keys. Lowercase the key with &lt;code&gt;c[tolower($1)]++&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trailing spaces or Windows line endings.&lt;/strong&gt; A value with a hidden &lt;code&gt;\r&lt;/code&gt; or a trailing space is a different key from the same value without it. Clean it first, for example with &lt;code&gt;sub(/\r$/, "")&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output order changes between machines.&lt;/strong&gt; It comes from the &lt;code&gt;for-in&lt;/code&gt; loop. Sort the output.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I count occurrences of a word in a file with AWK?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;awk '{t=$0; n += gsub(/word/, "&amp;amp;", t)} END {print n+0}' file&lt;/code&gt;. It counts every occurrence, including several on one line. To count only lines, use &lt;code&gt;awk '/word/ {c++} END {print c+0}' file&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I count how many times each value appears in a column?
&lt;/h3&gt;

&lt;p&gt;Use an array: &lt;code&gt;awk '{c[$1]++} END {for (k in c) print c[k], k}' file | sort -nr&lt;/code&gt;. Replace &lt;code&gt;$1&lt;/code&gt; with the column you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between &lt;code&gt;grep -c&lt;/code&gt; and counting with AWK?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;grep -c&lt;/code&gt; counts matching lines only. AWK can also count every occurrence (&lt;code&gt;gsub&lt;/code&gt;), count per value (arrays), and add conditions on specific columns.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I count unique values in a column?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;awk '!seen[$1]++ {n++} END {print n}' file&lt;/code&gt;, or collect the keys in an array and print its &lt;code&gt;length()&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/awk-count-occurrences" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>awk</category>
      <category>linux</category>
      <category>shell</category>
    </item>
    <item>
      <title>AWK Cheat Sheet: Commands, Variables, Functions &amp; One-Liners</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Sat, 31 Oct 2026 05:12:54 +0000</pubDate>
      <link>https://dev.to/devtalk94/awk-cheat-sheet-commands-variables-functions-one-liners-4c2e</link>
      <guid>https://dev.to/devtalk94/awk-cheat-sheet-commands-variables-functions-one-liners-4c2e</guid>
      <description>&lt;p&gt;This is a reference to keep open while you work: the AWK syntax you use most, grouped by task, with the result of each example. Every entry was run on gawk 5.2.1 and mawk 1.3.4. Where the two disagree, the entry says so, and a portability table near the end collects the differences in one place. Print this page from your browser if you want a paper copy.&lt;/p&gt;

&lt;p&gt;If you are new to AWK, read the &lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;beginner's guide&lt;/a&gt; first. This sheet assumes you know what a field and a record are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample files used below
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'start\nx\ny\nend\nz\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rng.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'one two\nthree four five\n\nsix\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; t4.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'p1 l1\np1 l2\n\np2 l1\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; para.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run AWK
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Program on the command line&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk 'pattern {action}' file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read from a pipe&lt;/td&gt;
&lt;td&gt;`command \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field separator&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;awk -F, '...' file&lt;/code&gt; (details in &lt;a href="https://dev-talk.com/awk-field-separator" rel="noopener noreferrer"&gt;AWK field separators&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set a variable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk -v name=value '...' file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Program from a file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk -f script.awk file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show the version&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk --version&lt;/code&gt; or &lt;code&gt;awk -W version&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wrap the program in single quotes. Double quotes let the shell replace &lt;code&gt;$1&lt;/code&gt; before AWK sees it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Program structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight awk"&gt;&lt;code&gt;&lt;span class="kr"&gt;BEGIN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;          &lt;span class="c1"&gt;# once, before any input&lt;/span&gt;
&lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;     &lt;span class="c1"&gt;# for every line that matches the pattern&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;             &lt;span class="c1"&gt;# no pattern: every line&lt;/span&gt;
&lt;span class="nx"&gt;pattern&lt;/span&gt;                &lt;span class="c1"&gt;# no action: print the matching lines&lt;/span&gt;
&lt;span class="kr"&gt;END&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;            &lt;span class="c1"&gt;# once, after the last line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fields and built-in variables
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The whole current line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, ...&lt;/td&gt;
&lt;td&gt;Field 1, field 2, ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of fields on this line (&lt;code&gt;$NF&lt;/code&gt; is the last, &lt;code&gt;$(NF-1)&lt;/code&gt; the one before)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line number across all input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FNR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line number within the current file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FILENAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the current file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Input field separator (default: runs of spaces and tabs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OFS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output field separator, used between items in &lt;code&gt;print a, b&lt;/code&gt; (default: a space)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Input record separator (default: newline). &lt;code&gt;RS=""&lt;/code&gt; reads paragraphs separated by blank lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ORS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output record separator (default: newline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ENVIRON["NAME"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An environment variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ARGC&lt;/code&gt;, &lt;code&gt;ARGV[i]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The command-line arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;RSTART&lt;/code&gt;, &lt;code&gt;RLENGTH&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Set by &lt;code&gt;match()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUBSEP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Joins the parts of a multi-dimensional array key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print FILENAME, NR, FNR}'&lt;/span&gt; t4.txt rng.txt | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'3,6p'&lt;/span&gt;
&lt;span class="c"&gt;# t4.txt 3 3&lt;/span&gt;
&lt;span class="c"&gt;# t4.txt 4 4&lt;/span&gt;
&lt;span class="c"&gt;# rng.txt 5 1&lt;/span&gt;
&lt;span class="c"&gt;# rng.txt 6 2&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{RS=""} {print NR ": " $1, NF}'&lt;/span&gt; para.txt
&lt;span class="c"&gt;# 1: p1 4&lt;/span&gt;
&lt;span class="c"&gt;# 2: p2 2&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{ORS="|"} {print $1} END{printf "\n"}'&lt;/span&gt; t4.txt
&lt;span class="c"&gt;# one|three||six|&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a b c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{NF=2; print}'&lt;/span&gt;        &lt;span class="c"&gt;# a b    (assigning NF cuts the line)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a b'&lt;/span&gt;   | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{$5="e"; print NF}'&lt;/span&gt;    &lt;span class="c"&gt;# 5      (assigning past the end adds empty fields)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Patterns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Selects&lt;/th&gt;
&lt;th&gt;Result on the sample files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NR==2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line number 2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk 'NR==2' t4.txt&lt;/code&gt; gives &lt;code&gt;three four five&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NR%2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Odd lines&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rng.txt&lt;/code&gt;: &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;z&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NR%2==0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Even lines&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rng.txt&lt;/code&gt;: &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NR&amp;gt;=2 &amp;amp;&amp;amp; NR&amp;lt;=3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A range of line numbers&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rng.txt&lt;/code&gt;: &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/start/,/end/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;From a matching line to the next matching line&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rng.txt&lt;/code&gt;: &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/regex/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lines matching a regular expression&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '/x/{c++} END{print c+0}' rng.txt&lt;/code&gt; gives &lt;code&gt;1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!/regex/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lines that do not match&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '!/x/' rng.txt&lt;/code&gt; skips &lt;code&gt;x&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$1 ~ /^t/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Field 1 matches&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;t4.txt&lt;/code&gt;: &lt;code&gt;three four five&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$1 !~ /^t/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Field 1 does not match&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;t4.txt&lt;/code&gt;: &lt;code&gt;one two&lt;/code&gt;, the blank line, &lt;code&gt;six&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-empty lines&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;t4.txt&lt;/code&gt;: 3 lines (the blank one is dropped)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!NF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blank lines&lt;/td&gt;
&lt;td&gt;`awk '!NF' t4.txt \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;length &amp;gt; 8&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Lines longer than 8 characters&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;t4.txt&lt;/code&gt;: &lt;code&gt;three four five&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Operators
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Operators&lt;/th&gt;
&lt;th&gt;Example and result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Arithmetic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;+ - * / % ^&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;2^10&lt;/code&gt; is &lt;code&gt;1024&lt;/code&gt;, &lt;code&gt;7%3&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;7/2&lt;/code&gt; is &lt;code&gt;3.5&lt;/code&gt;, &lt;code&gt;int(7/2)&lt;/code&gt; is &lt;code&gt;3&lt;/code&gt;, &lt;code&gt;-7%3&lt;/code&gt; is &lt;code&gt;-1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assignment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;= += -= *= /= ++ --&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;x=5; x++; x+=10; x*=2&lt;/code&gt; leaves &lt;code&gt;x&lt;/code&gt; at &lt;code&gt;32&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparison&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt; &amp;lt;= == != &amp;gt;= &amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;("10" &amp;lt; "9")&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt; (text compare), &lt;code&gt;(10 &amp;lt; 9)&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt; (number compare)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concatenation&lt;/td&gt;
&lt;td&gt;(nothing between values)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a="foo"; b="bar"&lt;/code&gt;: &lt;code&gt;a b&lt;/code&gt; is &lt;code&gt;foobar&lt;/code&gt;, &lt;code&gt;a "-" b&lt;/code&gt; is &lt;code&gt;foo-bar&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cond ? yes : no&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;n=5; (n%2==0 ? "even" : "odd")&lt;/code&gt; gives &lt;code&gt;odd&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Membership&lt;/td&gt;
&lt;td&gt;&lt;code&gt;key in array&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;("k" in a)&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;("z" in a)&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logic&lt;/td&gt;
&lt;td&gt;`&amp;amp;&amp;amp; \&lt;/td&gt;
&lt;td&gt;\&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The {% raw %}&lt;code&gt;**&lt;/code&gt; power operator works in gawk only; mawk gives a syntax error. Use &lt;code&gt;^&lt;/code&gt;, which works in both.&lt;/p&gt;

&lt;h2&gt;
  
  
  String functions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Does&lt;/th&gt;
&lt;th&gt;Example and result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;length(s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Length of a string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;length("Hello, World")&lt;/code&gt; is &lt;code&gt;12&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;substr(s, start, len)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part of a string, counting from 1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;substr(s,8)&lt;/code&gt; is &lt;code&gt;World&lt;/code&gt;, &lt;code&gt;substr(s,1,5)&lt;/code&gt; is &lt;code&gt;Hello&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;index(s, t)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Position of &lt;code&gt;t&lt;/code&gt; in &lt;code&gt;s&lt;/code&gt;, or 0&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;index(s,"World")&lt;/code&gt; is &lt;code&gt;8&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;toupper(s)&lt;/code&gt;, &lt;code&gt;tolower(s)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Change case&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;HELLO, WORLD&lt;/code&gt; and &lt;code&gt;hello, world&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sub(re, repl, s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace the first match in &lt;code&gt;s&lt;/code&gt;; returns 1 or 0&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s="foo bar foo"&lt;/code&gt; becomes &lt;code&gt;X bar foo&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gsub(re, repl, s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace every match; returns the count&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s="foo bar foo"&lt;/code&gt; becomes &lt;code&gt;X bar X&lt;/code&gt; (returns &lt;code&gt;2&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;match(s, re)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find a match; sets &lt;code&gt;RSTART&lt;/code&gt; and &lt;code&gt;RLENGTH&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;On &lt;code&gt;price: 42 USD&lt;/code&gt; with &lt;code&gt;/[0-9]+/&lt;/code&gt;: &lt;code&gt;8 2&lt;/code&gt;, and &lt;code&gt;substr&lt;/code&gt; gives &lt;code&gt;42&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;split(s, arr, sep)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split into an array; returns the count&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;split("a,b,c",p,",")&lt;/code&gt; returns &lt;code&gt;3&lt;/code&gt;, with &lt;code&gt;p[1]="a"&lt;/code&gt;, &lt;code&gt;p[3]="c"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sprintf(fmt, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format like &lt;code&gt;printf&lt;/code&gt;, return a string&lt;/td&gt;
&lt;td&gt;`sprintf("%05.1f\&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;{% raw %}&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{s="a-b-c"; gsub(/-/,"&amp;amp;&amp;amp;",s); print s}'&lt;/span&gt;                 &lt;span class="c"&gt;# a--b--c   (&amp;amp; means "the matched text")&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{n=split("a1b22c",p,/[0-9]+/); print n, p[1], p[2], p[3]}'&lt;/span&gt;  &lt;span class="c"&gt;# 3 a b c   (separator can be a regex)&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{s="  trim me  "; gsub(/^ +| +$/,"",s); print "[" s "]"}'&lt;/span&gt;   &lt;span class="c"&gt;# [trim me]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to remember. First, a string used as a pattern is a regular expression, so &lt;code&gt;gsub(".", "-", s)&lt;/code&gt; replaces every character, not every dot. On &lt;code&gt;a.b.c&lt;/code&gt; it gave &lt;code&gt;-----&lt;/code&gt;, while &lt;code&gt;gsub(/\./, "-", t)&lt;/code&gt; gave &lt;code&gt;a-b-c&lt;/code&gt;. Second, &lt;code&gt;substr()&lt;/code&gt; with a start below 1 is not portable: &lt;code&gt;substr("hello", -1, 3)&lt;/code&gt; gave &lt;code&gt;hel&lt;/code&gt; in gawk and &lt;code&gt;hell&lt;/code&gt; in mawk. Keep the start at 1 or more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers and printf
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;int(x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;int(3.9)&lt;/code&gt; is &lt;code&gt;3&lt;/code&gt;, &lt;code&gt;int(-3.9)&lt;/code&gt; is &lt;code&gt;-3&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sqrt(x)&lt;/code&gt;, &lt;code&gt;exp(x)&lt;/code&gt;, &lt;code&gt;log(x)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sqrt(16)&lt;/code&gt; is &lt;code&gt;4&lt;/code&gt;, &lt;code&gt;exp(0)&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;log(1)&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sin(x)&lt;/code&gt;, &lt;code&gt;cos(x)&lt;/code&gt;, &lt;code&gt;atan2(y,x)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sin(0)&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;cos(0)&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;atan2(0,-1)&lt;/code&gt; is &lt;code&gt;3.14159&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rand()&lt;/code&gt;, &lt;code&gt;srand(seed)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rand()&lt;/code&gt; returns a number from 0 up to (not including) 1. The actual numbers differ between awks, so do not compare them&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plain &lt;code&gt;print&lt;/code&gt; shows at most six significant digits, so use &lt;code&gt;printf&lt;/code&gt; when exact output matters. A single line that uses most formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{printf "%d %5.2f %-6s| %05d %x %o %e %c %%\n", 42.9, 3.14159, "ab", 42, 255, 8, 12345.678, 65}'&lt;/span&gt;
&lt;span class="c"&gt;# 42  3.14 ab    | 00042 ff 10 1.234568e+04 A %&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integer (42.9 prints as 42)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%5.2f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decimal number, width 5, 2 digits after the point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%-6s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String, width 6, left-aligned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%05d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integer padded with zeros to width 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;%x&lt;/code&gt;, &lt;code&gt;%o&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Hexadecimal, octal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scientific notation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Character from a number (65 gives A)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A literal percent sign&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;printf&lt;/code&gt; does not add a newline. End the format with &lt;code&gt;\n&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Control flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{for(i=1;i&amp;lt;=3;i++) printf "%d ", i; print ""}'&lt;/span&gt;                       &lt;span class="c"&gt;# 1 2 3&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{i=1; while(i&amp;lt;=3){printf "%d ", i; i++} print ""}'&lt;/span&gt;                  &lt;span class="c"&gt;# 1 2 3&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{i=5; do {printf "%d ", i; i++} while(i&amp;lt;3); print ""}'&lt;/span&gt;              &lt;span class="c"&gt;# 5   (do-while runs once first)&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{for(i=1;i&amp;lt;=10;i++){if(i%2) continue; if(i&amp;gt;6) break; printf "%d ", i} print ""}'&lt;/span&gt;   &lt;span class="c"&gt;# 2 4 6&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{x=7; if (x&amp;lt;5) print "low"; else if (x&amp;lt;10) print "mid"; else print "high"}'&lt;/span&gt;          &lt;span class="c"&gt;# mid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Statement&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;next&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop with this line and go to the next one. &lt;code&gt;NR==2{next} {print}&lt;/code&gt; skips line 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nextfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip the rest of the current file. &lt;code&gt;FNR==2{nextfile} {print FILENAME": "$0}&lt;/code&gt; printed one line from each of two files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exit n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop and set the exit status. &lt;code&gt;NR==2{exit 3}&lt;/code&gt; exited with 3. An &lt;code&gt;END&lt;/code&gt; block still runs after &lt;code&gt;exit&lt;/code&gt;: &lt;code&gt;BEGIN{exit 5} END{print "end still runs"}&lt;/code&gt; printed that text and exited with 5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Arrays
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Set and read&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a["key"]=1; print a["key"]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test for a key&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;if ("key" in a) ...&lt;/code&gt; (does not create the key)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count the entries&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;length(a)&lt;/code&gt; (works in gawk and mawk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove one key / all keys&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;delete a["key"]&lt;/code&gt; / &lt;code&gt;delete a&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loop over keys&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;for (k in a) print k, a[k]&lt;/code&gt; (order is not guaranteed, pipe to &lt;code&gt;sort&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split into an array&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;n=split("c a b", w)&lt;/code&gt; gives &lt;code&gt;w[1]="c"&lt;/code&gt;, &lt;code&gt;w[2]="a"&lt;/code&gt;, &lt;code&gt;w[3]="b"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two-part key&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a[1,2]="v"&lt;/code&gt;; split the key back with &lt;code&gt;split(k, p, SUBSEP)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count by value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{c[$1]++} END {for (k in c) print k, c[k]}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove duplicate lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;!seen[$0]++&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Input and output
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Write to a file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;print $1 &amp;gt; "out.txt"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipe to a command&lt;/td&gt;
&lt;td&gt;`print $1 \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write to standard error&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;print "message" &amp;gt; "/dev/stderr"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a shell command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;system("echo from shell")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read a command's output&lt;/td&gt;
&lt;td&gt;`"echo hello" \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read a file line by line&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;while ((getline line &amp;lt; "rng.txt") &amp;gt; 0) n++&lt;/code&gt; counted 5 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Close a file with &lt;code&gt;close("out.txt")&lt;/code&gt; before you read it back in the same program. I wrote &lt;code&gt;$1&lt;/code&gt; of every line of &lt;code&gt;t4.txt&lt;/code&gt; to a file, closed it, and read it back with &lt;code&gt;getline&lt;/code&gt;, and got &lt;code&gt;one&lt;/code&gt;, &lt;code&gt;three&lt;/code&gt;, an empty line and &lt;code&gt;six&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  User-defined functions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight awk"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tmp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;# extra parameters are local variables&lt;/span&gt;

&lt;span class="kr"&gt;BEGIN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;          &lt;span class="c1"&gt;# 9 b&lt;/span&gt;
&lt;span class="kr"&gt;BEGIN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nx"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;                           &lt;span class="c1"&gt;# 120&lt;/span&gt;
&lt;span class="kr"&gt;BEGIN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"keep"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;tmp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;           &lt;span class="c1"&gt;# 8 keep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWK has no &lt;code&gt;local&lt;/code&gt; keyword. Extra parameters you never pass behave as local variables, which is why &lt;code&gt;tmp&lt;/code&gt; kept its value in the last line. By habit, put two spaces before them to show they are locals.&lt;/p&gt;

&lt;h2&gt;
  
  
  One-liners
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Number the lines&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '{print NR": "$0}' rng.txt&lt;/code&gt; (first line: &lt;code&gt;1: start&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print line 3&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk 'NR==3' rng.txt&lt;/code&gt; (&lt;code&gt;y&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print lines 2 to 3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk 'NR&amp;gt;=2 &amp;amp;&amp;amp; NR&amp;lt;=3' rng.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print everything after line n&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk -v n=2 'NR&amp;gt;n' rng.txt&lt;/code&gt; (&lt;code&gt;y&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt;, &lt;code&gt;z&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print the last line&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk 'END{print}' rng.txt&lt;/code&gt; (&lt;code&gt;z&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print every other line&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk 'NR%2' rng.txt&lt;/code&gt; (&lt;code&gt;start&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;z&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete blank lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk 'NF' t4.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove duplicate lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk '!seen[$0]++' file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reverse the lines&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '{a[NR]=$0} END{for(i=NR;i&amp;gt;0;i--) print a[i]}' rng.txt&lt;/code&gt; (starts &lt;code&gt;z&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Join lines with commas&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '{printf "%s%s", sep, $0; sep=","} END{print ""}' rng.txt&lt;/code&gt; (&lt;code&gt;start,x,y,end,z&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trim leading and trailing space&lt;/td&gt;
&lt;td&gt;`awk '{gsub(/^[ \t]+\&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find the longest line&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;awk '{if (length &amp;gt; max) {max=length; line=$0}} END{print max, line}' t4.txt&lt;/code&gt; (&lt;code&gt;15 three four five&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count lines and words&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '{w+=NF} END{print NR, w}' t4.txt&lt;/code&gt; (&lt;code&gt;4 6&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swap the first two fields&lt;/td&gt;
&lt;td&gt;`echo 'a b c' \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capitalise the first word&lt;/td&gt;
&lt;td&gt;{% raw %}`echo 'hello world' \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print all but the first field&lt;/td&gt;
&lt;td&gt;{% raw %}`echo 'a b c' \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count matching lines&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;awk '/x/{c++} END{print c+0}' rng.txt&lt;/code&gt; (&lt;code&gt;1&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sum a column&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '{s+=$1} END{print s}' file&lt;/code&gt; (see &lt;a href="https://dev-talk.com/awk-sum-column" rel="noopener noreferrer"&gt;summing a column&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print a column&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;awk '{print $2}' file&lt;/code&gt; (see &lt;a href="https://dev-talk.com/awk-print-column" rel="noopener noreferrer"&gt;printing a column&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What works in gawk but not in mawk
&lt;/h2&gt;

&lt;p&gt;Most of this sheet runs on every awk. These features do not. The worst ones fail with no message at all, so your script runs and gives wrong output.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;gawk&lt;/th&gt;
&lt;th&gt;mawk 1.3.4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gensub()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Error: function never defined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;asort()&lt;/code&gt;, &lt;code&gt;asorti()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Work&lt;/td&gt;
&lt;td&gt;Error: function never defined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;**&lt;/code&gt; power operator&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Syntax error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IGNORECASE=1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Case-insensitive matching&lt;/td&gt;
&lt;td&gt;Silently ignored&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROCINFO["sorted_in"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sorts &lt;code&gt;for (k in a)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Silently ignored (loop order differs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;BEGINFILE&lt;/code&gt;, &lt;code&gt;ENDFILE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Run per file&lt;/td&gt;
&lt;td&gt;Silently do nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;FIELDWIDTHS&lt;/code&gt;, &lt;code&gt;FPAT&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Work&lt;/td&gt;
&lt;td&gt;Silently ignored&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF-8 characters&lt;/td&gt;
&lt;td&gt;With a UTF-8 locale, &lt;code&gt;length("héllo")&lt;/code&gt; is &lt;code&gt;5&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;6&lt;/code&gt; (counts bytes), even in a UTF-8 locale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Division by zero&lt;/td&gt;
&lt;td&gt;Fatal error&lt;/td&gt;
&lt;td&gt;Prints &lt;code&gt;-nan&lt;/code&gt; and carries on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x1A + 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;26&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-i inplace&lt;/code&gt;, &lt;code&gt;--csv&lt;/code&gt; (5.3+)&lt;/td&gt;
&lt;td&gt;gawk options (&lt;code&gt;-i inplace&lt;/code&gt; was run on gawk 5.2.1; &lt;code&gt;--csv&lt;/code&gt; needs 5.3+ and was not run)&lt;/td&gt;
&lt;td&gt;Not tested&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These do work in both: &lt;code&gt;length(array)&lt;/code&gt;, &lt;code&gt;nextfile&lt;/code&gt;, &lt;code&gt;delete array&lt;/code&gt;, &lt;code&gt;systime()&lt;/code&gt;, &lt;code&gt;strftime()&lt;/code&gt;, &lt;code&gt;/dev/stderr&lt;/code&gt;, &lt;code&gt;-v&lt;/code&gt; and &lt;code&gt;-f&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double quotes around the program.&lt;/strong&gt; &lt;code&gt;awk "{print $1}"&lt;/code&gt; prints whole lines, because the shell empties &lt;code&gt;$1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Header row in a numeric test.&lt;/strong&gt; &lt;code&gt;$4 &amp;gt; 5&lt;/code&gt; also matches a header like &lt;code&gt;qty&lt;/code&gt;. Add &lt;code&gt;NR&amp;gt;1 &amp;amp;&amp;amp;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Array loop order.&lt;/strong&gt; &lt;code&gt;for (k in a)&lt;/code&gt; has no guaranteed order. Pipe to &lt;code&gt;sort&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setting&lt;/strong&gt;&lt;code&gt;FS&lt;/code&gt;&lt;strong&gt;inside a rule.&lt;/strong&gt; It only affects the next line. Set it in &lt;code&gt;BEGIN&lt;/code&gt; or use &lt;code&gt;-F&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(NF-1)&lt;/code&gt;&lt;strong&gt;on a blank line.&lt;/strong&gt; It stops AWK with an error. Guard with &lt;code&gt;NF&amp;gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A string where you meant a regex.&lt;/strong&gt; &lt;code&gt;gsub(".", ...)&lt;/code&gt; matches any character.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/awk-cheat-sheet" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>awk</category>
      <category>linux</category>
      <category>shell</category>
      <category>commandline</category>
    </item>
    <item>
      <title>AWK Sum Column: Total, Average, Min, Max and Group By</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Fri, 30 Oct 2026 15:11:33 +0000</pubDate>
      <link>https://dev.to/devtalk94/awk-sum-column-total-average-min-max-and-group-by-20lb</link>
      <guid>https://dev.to/devtalk94/awk-sum-column-total-average-min-max-and-group-by-20lb</guid>
      <description>&lt;p&gt;Adding up a column of numbers is one of the best reasons to learn AWK. There is no spreadsheet to open and no script to write: one line reads the file, adds each value to a running total, and prints the result at the end. This guide starts with that one line and then covers the things people actually need next: average, minimum and maximum, totals per group, and what to do when the data is messy. Every example comes with the input and the exact output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sum += $1} END {print sum}'&lt;/span&gt; numbers.txt                &lt;span class="c"&gt;# sum of column 1&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {sum += $4} END {print sum}'&lt;/span&gt; sales.csv         &lt;span class="c"&gt;# CSV: skip header, sum column 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;New to AWK? The &lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;beginner's guide&lt;/a&gt; explains the &lt;code&gt;pattern {action}&lt;/code&gt; idea used here, and &lt;a href="https://dev-talk.com/awk-print-column" rel="noopener noreferrer"&gt;how to print a column&lt;/a&gt; explains &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$4&lt;/code&gt; and &lt;code&gt;NR&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample files
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'10\n20\n30.5\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; nums.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'100\n9\n25\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; n3.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'5\nN/A\n7\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bad.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'1 2 3\n4 5 6\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; grid.txt
: &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; empty.txt        &lt;span class="c"&gt;# an empty file&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sales.csv &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
date,region,product,qty,price
2026-10-01,north,pen,10,1.50
2026-10-01,south,book,3,12.00
2026-10-02,north,book,2,12.00
2026-10-02,east,pen,25,1.50
2026-10-03,south,pen,5,1.50
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sum a column
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sum += $1} END {print sum}'&lt;/span&gt; nums.txt
&lt;span class="c"&gt;# 60.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what happens. For every line, &lt;code&gt;sum += $1&lt;/code&gt; adds the first field to &lt;code&gt;sum&lt;/code&gt;. You never declared &lt;code&gt;sum&lt;/code&gt;: AWK creates a variable the first time you use it and starts it at zero. The &lt;code&gt;END&lt;/code&gt; block runs once, after the last line, and prints the total.&lt;/p&gt;

&lt;p&gt;For a CSV with a header, add &lt;code&gt;-F,&lt;/code&gt; for the separator and &lt;code&gt;NR&amp;gt;1&lt;/code&gt; to skip the header line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {s += $4} END {print s}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 45&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leaving out &lt;code&gt;NR&amp;gt;1&lt;/code&gt; does not change this total, because the header text &lt;code&gt;qty&lt;/code&gt; counts as 0. But it quietly breaks the next calculation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count rows and calculate the average
&lt;/h2&gt;

&lt;p&gt;Keep a counter next to the sum, then divide at the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {s += $4; n++} END {print s, n, s/n}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 45 5 9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now see what happens without the header skip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{s += $4; n++} END {print s, n, s/n}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 45 6 7.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sum is the same, but the header was counted as a sixth row, so the average is wrong and nothing warns you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Guard against an empty file
&lt;/h3&gt;

&lt;p&gt;If there is no input, &lt;code&gt;n&lt;/code&gt; is never set, and the division goes wrong. The two awks I tested disagree on how:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{s += $1; n++} END {print s/n}'&lt;/span&gt; empty.txt
&lt;span class="c"&gt;# gawk: fatal: division by zero attempted&lt;/span&gt;
&lt;span class="c"&gt;# mawk: -nan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gawk stops with an error. mawk prints &lt;code&gt;-nan&lt;/code&gt; and carries on, which is easy to miss in a script. Check before you divide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{s += $1; n++} END {if (n &amp;gt; 0) print s/n; else print "no data"}'&lt;/span&gt; empty.txt
&lt;span class="c"&gt;# no data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Minimum and maximum
&lt;/h2&gt;

&lt;p&gt;Start both from the first data row, then compare every row against them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR==2 {min=max=$4}
         NR&amp;gt;2  {if ($4&amp;lt;min) min=$4; if ($4&amp;gt;max) max=$4}
         END   {print "min", min, "max", max}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# min 2 max 25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comparison is numeric, not alphabetical. On the file &lt;code&gt;n3.txt&lt;/code&gt; (100, 9, 25), the minimum comes out as 9 and the maximum as 100, which is correct. If the comparison were on text, "9" would beat "100".&lt;/p&gt;

&lt;p&gt;Do not start the minimum at 0 in a &lt;code&gt;BEGIN&lt;/code&gt; block. Then nothing in a list of positive numbers is ever smaller, and you get 0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{min=0} {if ($1&amp;lt;min) min=$1} END {print min}'&lt;/span&gt; n3.txt
&lt;span class="c"&gt;# 0   (wrong, the real minimum is 9)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the data may not have a header, or you do not know the first row's line number, use a version that treats "not set yet" as the starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{if (min=="" || $1&amp;lt;min) min=$1; if (max=="" || $1&amp;gt;max) max=$1} END {print min, max}'&lt;/span&gt; n3.txt
&lt;span class="c"&gt;# 9 100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sum only some rows
&lt;/h2&gt;

&lt;p&gt;Put a condition in front of the braces. Total quantity sold in the north region:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'$2=="north" {s += $4} END {print s}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To count matching rows, use a counter. Add &lt;code&gt;+0&lt;/code&gt; when you print it, so that zero matches prints &lt;code&gt;0&lt;/code&gt; instead of an empty line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 &amp;amp;&amp;amp; $2=="north" {n++} END {print n+0}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Totals per group
&lt;/h2&gt;

&lt;p&gt;An array indexed by the group name gives you one total per group. This is the AWK version of a spreadsheet pivot table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {s[$2] += $4} END {for (k in s) print k, s[k]}'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;span class="c"&gt;# east 25&lt;/span&gt;
&lt;span class="c"&gt;# north 12&lt;/span&gt;
&lt;span class="c"&gt;# south 8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;| sort&lt;/code&gt; is there because AWK does not promise any order when looping over an array; gawk and mawk printed the same groups in a different order when I left it out.&lt;/p&gt;

&lt;p&gt;The same idea works for money. Revenue is quantity times price, so add that product per region, plus a grand total:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {r[$2] += $4 * $5; t += $4 * $5}
         END {for (k in r) print k, r[k]; print "total", t}'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;span class="c"&gt;# east 37.5&lt;/span&gt;
&lt;span class="c"&gt;# north 39&lt;/span&gt;
&lt;span class="c"&gt;# south 43.5&lt;/span&gt;
&lt;span class="c"&gt;# total 120&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sum across each row, or down each column
&lt;/h2&gt;

&lt;p&gt;The file &lt;code&gt;grid.txt&lt;/code&gt; has two lines of three numbers. A loop over the fields gives the total of each row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{t=0; for (i=1; i&amp;lt;=NF; i++) t += $i; print t}'&lt;/span&gt; grid.txt
&lt;span class="c"&gt;# 6&lt;/span&gt;
&lt;span class="c"&gt;# 15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To total every column at once, keep an array indexed by column number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{for (i=1; i&amp;lt;=NF; i++) c[i] += $i}
     END {for (i=1; i&amp;lt;=NF; i++) printf "%s%s", c[i], (i&amp;lt;NF ? OFS : ORS)}'&lt;/span&gt; grid.txt
&lt;span class="c"&gt;# 5 7 9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;END&lt;/code&gt;, &lt;code&gt;NF&lt;/code&gt; still holds the field count of the last line, which is fine when every line has the same number of columns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running total
&lt;/h2&gt;

&lt;p&gt;Print inside the main rule, not in &lt;code&gt;END&lt;/code&gt;, to show the total so far on every line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {s += $4; print $4, s}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# 10 10&lt;/span&gt;
&lt;span class="c"&gt;# 3 13&lt;/span&gt;
&lt;span class="c"&gt;# 2 15&lt;/span&gt;
&lt;span class="c"&gt;# 25 40&lt;/span&gt;
&lt;span class="c"&gt;# 5 45&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Percent of total (reading the file twice)
&lt;/h2&gt;

&lt;p&gt;To show each row as a share of the total, AWK needs the total first. Pass the same file twice. &lt;code&gt;NR==FNR&lt;/code&gt; is true only while the first copy is being read, so you can add things up on the first pass and print on the second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR==FNR { if (FNR&amp;gt;1) t += $4; next }
         FNR&amp;gt;1  { printf "%-5s %5.1f%%\n", $3, $4/t*100 }'&lt;/span&gt; sales.csv sales.csv
&lt;span class="c"&gt;# pen    22.2%&lt;/span&gt;
&lt;span class="c"&gt;# book    6.7%&lt;/span&gt;
&lt;span class="c"&gt;# book    4.4%&lt;/span&gt;
&lt;span class="c"&gt;# pen    55.6%&lt;/span&gt;
&lt;span class="c"&gt;# pen    11.1%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Print the result properly
&lt;/h2&gt;

&lt;p&gt;AWK's plain &lt;code&gt;print&lt;/code&gt; shows a number with at most six significant digits, so a large decimal total turns into scientific notation. I tested this on both awks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN {print 1234567.89; printf "%.2f\n", 1234567.89}'&lt;/span&gt;
&lt;span class="c"&gt;# 1.23457e+06&lt;/span&gt;
&lt;span class="c"&gt;# 1234567.89&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For money and anything you plan to copy into a report, use &lt;code&gt;printf "%.2f\n"&lt;/code&gt;. Whole numbers are fine either way (&lt;code&gt;100000 * 3&lt;/code&gt; prints &lt;code&gt;300000&lt;/code&gt;), and &lt;code&gt;0.1 + 0.2&lt;/code&gt; prints &lt;code&gt;0.3&lt;/code&gt; because of the same six-digit rounding.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the data is messy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Text where a number should be
&lt;/h3&gt;

&lt;p&gt;AWK converts text to the number it starts with, and to 0 if it starts with no number. A row of &lt;code&gt;N/A&lt;/code&gt; is added as 0, with no error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{s += $1; n++} END {print s, n, s/n}'&lt;/span&gt; bad.txt
&lt;span class="c"&gt;# 12 3 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sum is right, but the average divides by three rows when only two have numbers. Filter with a pattern, and the count and the sum both agree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$1 ~ /^[0-9.]+$/ {s += $1; n++} END {print s, n, s/n}'&lt;/span&gt; bad.txt
&lt;span class="c"&gt;# 12 2 6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Currency symbols and thousand separators
&lt;/h3&gt;

&lt;p&gt;A value like &lt;code&gt;$1,200.50&lt;/code&gt; starts with a dollar sign, so AWK reads it as 0. Strip the symbol and the comma first with &lt;code&gt;gsub&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'price\n$1,200.50\n$99.25\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; money.txt
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{s += $1} END {print s}'&lt;/span&gt; money.txt
&lt;span class="c"&gt;# 0&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {gsub(/[$,]/, "", $1); s += $1} END {printf "%.2f\n", s}'&lt;/span&gt; money.txt
&lt;span class="c"&gt;# 1299.75&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(I skipped the &lt;code&gt;price&lt;/code&gt; header with &lt;code&gt;NR&amp;gt;1&lt;/code&gt;; it would add 0 either way.) A comma used as a thousands separator also clashes with &lt;code&gt;-F,&lt;/code&gt;. A quoted &lt;code&gt;"1,200"&lt;/code&gt; in a CSV file is split into two fields, which is a job for the CSV article in this series.&lt;/p&gt;

&lt;h3&gt;
  
  
  Numbers with a unit
&lt;/h3&gt;

&lt;p&gt;AWK reads the leading number and ignores the rest, so &lt;code&gt;20G&lt;/code&gt; plus &lt;code&gt;120G&lt;/code&gt; adds to 140:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Used\n20G\n0\n120G\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {s += $1+0} END {print s}'&lt;/span&gt;
&lt;span class="c"&gt;# 140&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works only when every value has the same unit. A column that mixes &lt;code&gt;M&lt;/code&gt; and &lt;code&gt;G&lt;/code&gt; will add the digits as if they were the same thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strings that look like hex
&lt;/h3&gt;

&lt;p&gt;One difference between the two awks: a value such as &lt;code&gt;0x1A&lt;/code&gt; is 0 in gawk and 26 in mawk when used as a number. If your data contains hex-looking codes, do not rely on arithmetic on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sum&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk '{s+=$1} END {print s}'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sum, skipping a header&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk 'NR&amp;gt;1 {s+=$1} END {print s}'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk '{s+=$1; n++} END {if (n) print s/n}'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk 'END {print NR}'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max&lt;/td&gt;
&lt;td&gt;`awk '{if (max=="" \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Min&lt;/td&gt;
&lt;td&gt;{% raw %}`awk '{if (min=="" \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sum per group&lt;/td&gt;
&lt;td&gt;{% raw %}`awk '{s[$1]+=$2} END {for (k in s) print k, s[k]}' \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two decimals&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;printf "%.2f\n", s&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I sum a column in AWK?
&lt;/h3&gt;

&lt;p&gt;Add each value to a variable in the main rule and print it in &lt;code&gt;END&lt;/code&gt;: &lt;code&gt;awk '{s += $3} END {print s}' file&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I sum a column in a CSV file?
&lt;/h3&gt;

&lt;p&gt;Add the separator and skip the header: &lt;code&gt;awk -F, 'NR&amp;gt;1 {s += $4} END {print s}' file.csv&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does my total print as 1.23457e+06?
&lt;/h3&gt;

&lt;p&gt;Plain &lt;code&gt;print&lt;/code&gt; limits numbers to six significant digits. Use &lt;code&gt;printf "%.2f\n", total&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can AWK sum a column that has a header?
&lt;/h3&gt;

&lt;p&gt;Yes. Skip the first line with &lt;code&gt;NR&amp;gt;1&lt;/code&gt;, so the header is neither added nor counted.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/awk-sum-column" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>awk</category>
      <category>linux</category>
      <category>shell</category>
      <category>commandline</category>
    </item>
    <item>
      <title>AWK Field Separator: -F, FS and Multiple Delimiters</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Fri, 30 Oct 2026 10:55:36 +0000</pubDate>
      <link>https://dev.to/devtalk94/awk-field-separator-f-fs-and-multiple-delimiters-4bn6</link>
      <guid>https://dev.to/devtalk94/awk-field-separator-f-fs-and-multiple-delimiters-4bn6</guid>
      <description>&lt;p&gt;The most common reason an AWK command prints the wrong thing is that AWK split the line somewhere you did not expect. The rule that decides where to split is the &lt;strong&gt;field separator&lt;/strong&gt;. Get it right and &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt; and &lt;code&gt;$NF&lt;/code&gt; point at the columns you meant. This guide covers the default behaviour, single and multiple delimiters, the four ways to set the separator, and a few traps, with sample input and the exact output of each example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; file.csv                 &lt;span class="c"&gt;# comma&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;: &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; /etc/passwd              &lt;span class="c"&gt;# colon&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; file.tsv              &lt;span class="c"&gt;# tab&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[,;]'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; file.txt            &lt;span class="c"&gt;# comma OR semicolon&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; file.txt              &lt;span class="c"&gt;# the two-character separator ", "&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are new to AWK, start with the &lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;beginner's guide&lt;/a&gt;. To see how column numbers work once the line is split, read &lt;a href="https://dev-talk.com/awk-print-column" rel="noopener noreferrer"&gt;how to print a column with AWK&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample files
&lt;/h2&gt;

&lt;p&gt;Create these once. Every example below uses them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'root:x:0:0:root:/root:/bin/bash\nada:x:1000:1000:Ada L:/home/ada:/bin/zsh\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; passwd.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'id\tname\tcity\n1\tAda Lovelace\tLondon\n2\tAlan Turing\tWilmslow\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; t.tsv
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a, b;c ,d\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mix.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a,,c,\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; empty.csv
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  one   two\tthree  \n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ws.txt
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'2026-10-01 ERROR disk full\n2026-10-02 INFO ok\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; app.log

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sales.csv &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
date,region,product,qty,price
2026-10-01,north,pen,10,1.50
2026-10-01,south,book,3,12.00
2026-10-02,north,book,2,12.00
2026-10-02,east,pen,25,1.50
2026-10-03,south,pen,5,1.50
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; access.log &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
10.0.0.1 - - [24/Oct/2026:10:00:01 +0000] "GET /index.php HTTP/1.1" 200 512
10.0.0.2 - - [24/Oct/2026:10:00:02 +0000] "GET /about HTTP/1.1" 404 128
10.0.0.1 - - [24/Oct/2026:10:00:03 +0000] "POST /login HTTP/1.1" 302 0
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What AWK does when you set nothing
&lt;/h2&gt;

&lt;p&gt;By default, fields are separated by runs of spaces and tabs, and leading and trailing whitespace is ignored. The file &lt;code&gt;ws.txt&lt;/code&gt; has two leading spaces, three spaces between words, a tab, and trailing spaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print NF, "[" $1 "]"}'&lt;/span&gt; ws.txt
&lt;span class="c"&gt;# 3 [one]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three fields, and the first one is clean. That is why plain &lt;code&gt;awk '{print $2}'&lt;/code&gt; works well on column-aligned output like &lt;code&gt;df&lt;/code&gt; or &lt;code&gt;ps&lt;/code&gt;, and why it fails on anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set a single-character separator
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;-F&lt;/code&gt; followed by the character. A single character other than a space is taken literally, so you do not need to escape it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;: &lt;span class="s1"&gt;'{print $1 " -&amp;gt; " $NF}'&lt;/span&gt; passwd.txt
&lt;span class="c"&gt;# root -&amp;gt; /bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# ada -&amp;gt; /bin/zsh&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'id|name|city\n1|Ada|London\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'|'&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $2}'&lt;/span&gt;
&lt;span class="c"&gt;# Ada&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a.b.c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;
&lt;span class="c"&gt;# b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipe and the dot are special characters in regular expressions, yet both worked as plain characters here. I tested that on gawk and mawk. Quote the pipe anyway, because the shell treats an unquoted &lt;code&gt;|&lt;/code&gt; as a pipe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tabs
&lt;/h3&gt;

&lt;p&gt;A tab-separated file needs &lt;code&gt;-F'\t'&lt;/code&gt;. Without it, a name like "Ada Lovelace" is split at the space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $2}'&lt;/span&gt; t.tsv
&lt;span class="c"&gt;# Ada Lovelace&lt;/span&gt;
&lt;span class="c"&gt;# Alan Turing&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $2}'&lt;/span&gt; t.tsv        &lt;span class="c"&gt;# default splitting&lt;/span&gt;
&lt;span class="c"&gt;# Ada&lt;/span&gt;
&lt;span class="c"&gt;# Alan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In bash you can also pass a real tab character with &lt;code&gt;-F$'\t'&lt;/code&gt;; it gave the same result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-character and multiple delimiters
&lt;/h2&gt;

&lt;p&gt;When the separator is more than one character, AWK treats it as a &lt;strong&gt;regular expression&lt;/strong&gt;. That is how you get several delimiters at once.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Input → result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exactly two characters&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F', '&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;x, y, z&lt;/code&gt; → &lt;code&gt;$2&lt;/code&gt; is &lt;code&gt;y&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comma or semicolon&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F'[,;]'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a,b;c&lt;/code&gt; → &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any run of spaces, commas, semicolons&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F'[ ,;]+'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a, b;c ,d&lt;/code&gt; → 4 fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A word as the separator&lt;/td&gt;
&lt;td&gt;`-F'one\&lt;/td&gt;
&lt;td&gt;two'`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delimiter with optional spaces around it&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F' *, *'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a, b ,c&lt;/code&gt; → &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, with &lt;code&gt;mix.txt&lt;/code&gt; (&lt;code&gt;a, b;c ,d&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[ ,;]+'&lt;/span&gt; &lt;span class="s1"&gt;'{print NF; print $1 "|" $2 "|" $3 "|" $4}'&lt;/span&gt; mix.txt
&lt;span class="c"&gt;# 4&lt;/span&gt;
&lt;span class="c"&gt;# a|b|c|d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trimming is the other common need. A plain &lt;code&gt;-F,&lt;/code&gt; on &lt;code&gt;a, b ,c&lt;/code&gt; leaves the spaces inside the field (&lt;code&gt;$2&lt;/code&gt; is &lt;code&gt;" b "&lt;/code&gt;). Put the spaces in the pattern and they disappear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a, b ,c'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;' *, *'&lt;/span&gt; &lt;span class="s1"&gt;'{print "[" $1 "][" $2 "][" $3 "]"}'&lt;/span&gt;
&lt;span class="c"&gt;# [a][b][c]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Empty fields and leading spaces
&lt;/h2&gt;

&lt;p&gt;With a single-character separator, every delimiter counts, even next to another one. The line &lt;code&gt;a,,c,&lt;/code&gt; has four fields, two of them empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{print NF; print "[" $2 "]"}'&lt;/span&gt; empty.csv
&lt;span class="c"&gt;# 4&lt;/span&gt;
&lt;span class="c"&gt;# []&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default splitting behaves differently: it collapses runs of whitespace. You can see the difference by asking for a single space as the separator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a  b'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print NF}'&lt;/span&gt;          &lt;span class="c"&gt;# 2   (default: runs collapse)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a  b'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[ ]'&lt;/span&gt; &lt;span class="s1"&gt;'{print NF}'&lt;/span&gt;  &lt;span class="c"&gt;# 3   (each space separates, so there is an empty field)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leads to a trap. If you use a regex like &lt;code&gt;[ ]+&lt;/code&gt; on a line that starts with spaces, you get an empty first field, which the default splitting would have skipped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'  a b'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print NF, "[" $1 "]"}'&lt;/span&gt;          &lt;span class="c"&gt;# 2 [a]&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'  a b'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[ ]+'&lt;/span&gt; &lt;span class="s1"&gt;'{print NF, "[" $1 "]"}'&lt;/span&gt;  &lt;span class="c"&gt;# 3 []&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you only want whitespace splitting, leave the separator alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four ways to set the separator
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Way&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Good for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-F&lt;/code&gt; option&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk -F, '...' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One-liners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v FS=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk -v FS=, '...' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same effect, handy in scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;BEGIN&lt;/code&gt; block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk 'BEGIN{FS=","} ...' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Program files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After the program&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk '...' FS=, file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Different separator per file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All four split &lt;code&gt;sales.csv&lt;/code&gt; on commas when I tested them. The last form is the odd one out, because the assignment takes effect when AWK reaches it in the argument list. That lets you read files with different formats in one run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; &lt;span class="nv"&gt;FS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;, sales.csv &lt;span class="nv"&gt;FS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;: passwd.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first file's lines print their first comma-separated field (&lt;code&gt;date&lt;/code&gt;, &lt;code&gt;2026-10-01&lt;/code&gt;, ...) and then, once AWK reaches the second assignment, &lt;code&gt;passwd.txt&lt;/code&gt; lines print their first colon-separated field (&lt;code&gt;root&lt;/code&gt;, &lt;code&gt;ada&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha: changing FS inside a rule is one line too late
&lt;/h2&gt;

&lt;p&gt;AWK splits a line into fields as soon as it reads it. If you set &lt;code&gt;FS&lt;/code&gt; in the main rule, the current line has already been split, so the change applies from the next line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{FS=":"; print $1}'&lt;/span&gt; passwd.txt
&lt;span class="c"&gt;# root:x:0:0:root:/root:/bin/bash     (first line: still split on whitespace)&lt;/span&gt;
&lt;span class="c"&gt;# ada&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set it in &lt;code&gt;BEGIN&lt;/code&gt; instead, or force a re-split with &lt;code&gt;$0=$0&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{FS=":"; $0=$0; print $1}'&lt;/span&gt; passwd.txt
&lt;span class="c"&gt;# root&lt;/span&gt;
&lt;span class="c"&gt;# ada&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  FS reads, OFS writes
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;FS&lt;/code&gt; controls how input is split. &lt;code&gt;OFS&lt;/code&gt; controls what goes between fields when you print with commas. Combine them to convert formats. Assigning to a field (even to itself, with &lt;code&gt;$1=$1&lt;/code&gt;) makes AWK rebuild the line using &lt;code&gt;OFS&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{FS=","; OFS=";"} {$1=$1; print}'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-2&lt;/span&gt;
&lt;span class="c"&gt;# date;region;product;qty;price&lt;/span&gt;
&lt;span class="c"&gt;# 2026-10-01;north;pen;10;1.50&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;OFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'{$1=$1} 1'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-2&lt;/span&gt; | &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt;
&lt;span class="c"&gt;# date^Iregion^Iproduct^Iqty^Iprice$        (^I is a tab, $ marks the line end)&lt;/span&gt;
&lt;span class="c"&gt;# 2026-10-01^Inorth^Ipen^I10^I1.50$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trailing &lt;code&gt;1&lt;/code&gt; is a pattern that is always true, so AWK prints the line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A timestamp inside square brackets
&lt;/h3&gt;

&lt;p&gt;In a web server log, the date sits between &lt;code&gt;[&lt;/code&gt; and &lt;code&gt;]&lt;/code&gt;. Use both brackets as separators. In a bracket expression, put &lt;code&gt;]&lt;/code&gt; first so it counts as a character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[][]'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; access.log
&lt;span class="c"&gt;# 24/Oct/2026:10:00:01 +0000&lt;/span&gt;
&lt;span class="c"&gt;# 24/Oct/2026:10:00:02 +0000&lt;/span&gt;
&lt;span class="c"&gt;# 24/Oct/2026:10:00:03 +0000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The quoted request line
&lt;/h3&gt;

&lt;p&gt;Splitting on the double quote isolates the request. Then &lt;code&gt;split()&lt;/code&gt; breaks it into method and path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="s1"&gt;'{split($2, r, " "); print r[1], r[2]}'&lt;/span&gt; access.log
&lt;span class="c"&gt;# GET /index.php&lt;/span&gt;
&lt;span class="c"&gt;# GET /about&lt;/span&gt;
&lt;span class="c"&gt;# POST /login&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A word as the separator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;' ERROR '&lt;/span&gt; &lt;span class="s1"&gt;'NF&amp;gt;1 {print $2}'&lt;/span&gt; app.log
&lt;span class="c"&gt;# disk full&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the first line contains &lt;code&gt;ERROR&lt;/code&gt;, so it is the only one with more than one field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Split into characters, or into fixed widths
&lt;/h2&gt;

&lt;p&gt;An empty separator splits every character into its own field. This worked on both gawk and mawk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;abc | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;FS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{print NF, $2}'&lt;/span&gt;
&lt;span class="c"&gt;# 3 b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For fixed-width columns, gawk has &lt;code&gt;FIELDWIDTHS&lt;/code&gt;. This is a gawk-only feature, and the failure on other awks is quiet. Create a one-line file whose first 10 characters are a date and next 5 are a level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'2026-10-01ERROR\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; fixed.txt
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{FIELDWIDTHS="10 5"} {print "[" $1 "][" $2 "]"}'&lt;/span&gt; fixed.txt
&lt;span class="c"&gt;# gawk: [2026-10-01][ERROR]&lt;/span&gt;
&lt;span class="c"&gt;# mawk: [2026-10-01ERROR][]        (setting ignored, no error message)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a script must run on mawk too, use &lt;code&gt;substr($0, 1, 10)&lt;/code&gt; and &lt;code&gt;substr($0, 11, 5)&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a separator is not enough: quoted CSV
&lt;/h2&gt;

&lt;p&gt;A separator cannot understand quoting. On the line &lt;code&gt;Ada,"hi, there"&lt;/code&gt;, &lt;code&gt;-F,&lt;/code&gt; gives you three fields, because the comma inside the quotes also splits. For real CSV with quoted fields you need &lt;code&gt;FPAT&lt;/code&gt; in gawk, or the built-in CSV mode that gawk added in version 5.3.0. I could only test gawk 5.2.1 here, so I have not run the CSV mode. The CSV article in this series covers it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input format&lt;/th&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spaces and tabs (default)&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comma&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F,&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Colon&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F:&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipe&lt;/td&gt;
&lt;td&gt;`-F'\&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tab&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;-F'\t'&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Either of several characters&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F'[,;]'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delimiter with spaces around it&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F' *, *'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Square brackets&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F'[][]'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Each character&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-v FS=&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I use multiple delimiters in AWK?
&lt;/h3&gt;

&lt;p&gt;Give &lt;code&gt;-F&lt;/code&gt; a regular expression: &lt;code&gt;-F'[,;]'&lt;/code&gt; for a comma or a semicolon, or &lt;code&gt;-F'one|two'&lt;/code&gt; for two words. Add a &lt;code&gt;+&lt;/code&gt; after the brackets to treat a run of them as one separator.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between &lt;code&gt;-F&lt;/code&gt; and &lt;code&gt;FS&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Nothing for your program. &lt;code&gt;-F,&lt;/code&gt; on the command line sets the same &lt;code&gt;FS&lt;/code&gt; variable that &lt;code&gt;BEGIN{FS=","}&lt;/code&gt; sets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does &lt;code&gt;$1&lt;/code&gt; print the whole line?
&lt;/h3&gt;

&lt;p&gt;Because the separator never matched. If you set &lt;code&gt;-F,&lt;/code&gt; but the file uses semicolons, there is only one field, and &lt;code&gt;$1&lt;/code&gt; is the whole line. Check with &lt;code&gt;awk -F, '{print NF}' file&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I set the separator to a tab?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;-F'\t'&lt;/code&gt;, or in bash &lt;code&gt;-F$'\t'&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/awk-field-separator" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>awk</category>
      <category>linux</category>
      <category>shell</category>
      <category>commandline</category>
    </item>
    <item>
      <title>AWK Print Column: $1, $NF, Last Column and More</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Fri, 30 Oct 2026 09:37:56 +0000</pubDate>
      <link>https://dev.to/devtalk94/awk-print-column-1-nf-last-column-and-more-3a68</link>
      <guid>https://dev.to/devtalk94/awk-print-column-1-nf-last-column-and-more-3a68</guid>
      <description>&lt;p&gt;Pulling one column out of a text file is the most common thing people use AWK for. It takes one short command, but a few small details decide whether you get the right column, a broken one, or a crash on a blank line. This guide covers the command, the options around it, and the mistakes worth knowing about, with sample input and the exact output of each example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick answer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; file.txt          &lt;span class="c"&gt;# second column, split on spaces and tabs&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; file.csv      &lt;span class="c"&gt;# second column of a comma-separated file&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $NF}'&lt;/span&gt; file.txt         &lt;span class="c"&gt;# last column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$1&lt;/code&gt; is the first column, &lt;code&gt;$2&lt;/code&gt; the second, and &lt;code&gt;$NF&lt;/code&gt; the last one. If you are new to AWK, read the &lt;a href="https://dev-talk.com/awk-command-linux-beginners-guide" rel="noopener noreferrer"&gt;AWK beginner's guide&lt;/a&gt; first for how lines and fields work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample files
&lt;/h2&gt;

&lt;p&gt;Create these once and every example below will work as shown. &lt;code&gt;sales.csv&lt;/code&gt; is the same file used in the rest of this series.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'a b c d'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; w.txt

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sales.csv &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
date,region,product,qty,price
2026-10-01,north,pen,10,1.50
2026-10-01,south,book,3,12.00
2026-10-02,north,book,2,12.00
2026-10-02,east,pen,25,1.50
2026-10-03,south,pen,5,1.50
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; df.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   20G   28G  42% /
tmpfs           2.0G     0  2.0G   0% /dev/shm
/dev/sdb1       200G  120G   70G  63% /data
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Print one or more columns
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; w.txt          &lt;span class="c"&gt;# b&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1, $3}'&lt;/span&gt; w.txt      &lt;span class="c"&gt;# a c&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1 $3}'&lt;/span&gt; w.txt       &lt;span class="c"&gt;# ac&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1 "-" $3}'&lt;/span&gt; w.txt   &lt;span class="c"&gt;# a-c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A comma inside &lt;code&gt;print&lt;/code&gt; inserts the output separator, which is a space by default. Without the comma the values are glued together. If you want to join them with something else, either put your own text between them (the last line) or change the output separator once in a &lt;code&gt;BEGIN&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN {OFS=","} {print $1, $3}'&lt;/span&gt; w.txt    &lt;span class="c"&gt;# a,c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Choose the delimiter
&lt;/h2&gt;

&lt;p&gt;AWK splits on runs of spaces and tabs unless you tell it otherwise. Use &lt;code&gt;-F&lt;/code&gt; for anything else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; sales.csv                 &lt;span class="c"&gt;# CSV: region, north, south ...&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;: &lt;span class="s1"&gt;'{print $1, $NF}'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'root:x:0:0:root:/root:/bin/bash'&lt;/span&gt;
&lt;span class="c"&gt;# root /bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; file.tsv               &lt;span class="c"&gt;# tab-separated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt; in the second example is a bash and zsh here-string that feeds a short text to the command; in plain &lt;code&gt;sh&lt;/code&gt;, use &lt;code&gt;echo '...' | awk ...&lt;/code&gt; instead. The details of separators, including several at once, are in the &lt;a href="https://dev-talk.com/awk-field-separator" rel="noopener noreferrer"&gt;field separator guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The last column and the one before it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;NF&lt;/code&gt; is the number of fields on the current line, so &lt;code&gt;$NF&lt;/code&gt; is the last field and &lt;code&gt;$(NF-1)&lt;/code&gt; is the one before it. This works even when lines have different numbers of columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $NF}'&lt;/span&gt; w.txt         &lt;span class="c"&gt;# d&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $(NF-1)}'&lt;/span&gt; w.txt     &lt;span class="c"&gt;# c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parentheses matter. Without them, AWK reads &lt;code&gt;$NF-1&lt;/code&gt; as "the last field, minus one":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'5 10'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $NF-1}'&lt;/span&gt;     &lt;span class="c"&gt;# 9, not the 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Print everything except the first column
&lt;/h2&gt;

&lt;p&gt;There is no "all but the first" shortcut, but there are three ways to do it. Setting &lt;code&gt;$1&lt;/code&gt; to an empty string leaves a stray leading space, because AWK rebuilds the line with its output separator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ $1=""; print }'&lt;/span&gt; w.txt                  &lt;span class="c"&gt;# " b c d"  (note the leading space)&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ $1=""; print substr($0,2) }'&lt;/span&gt; w.txt     &lt;span class="c"&gt;# b c d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loop version has no such problem, and it also lets you print any range of columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ for (i=2; i&amp;lt;=NF; i++) printf "%s%s", $i, (i&amp;lt;NF ? OFS : ORS) }'&lt;/span&gt; w.txt
&lt;span class="c"&gt;# b c d&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a b c d e f\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'{ for (i=from; i&amp;lt;=to; i++) printf "%s%s", $i, (i&amp;lt;to ? OFS : ORS) }'&lt;/span&gt;
&lt;span class="c"&gt;# b c d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Skip the header and filter rows
&lt;/h2&gt;

&lt;p&gt;Put a condition in front of the braces. &lt;code&gt;NR&amp;gt;1&lt;/code&gt; skips the first line; a field test keeps only some rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $1, $5}'&lt;/span&gt; df.txt
&lt;span class="c"&gt;# /dev/sda1 42%&lt;/span&gt;
&lt;span class="c"&gt;# tmpfs 0%&lt;/span&gt;
&lt;span class="c"&gt;# /dev/sdb1 63%&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'$2=="north" {print $3}'&lt;/span&gt; sales.csv
&lt;span class="c"&gt;# pen&lt;/span&gt;
&lt;span class="c"&gt;# book&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pick a column by its header name
&lt;/h2&gt;

&lt;p&gt;Counting columns gets fragile when someone adds one to the file. If the first line is a header, you can look columns up by name instead. Read the header into an array on line 1, then use the array as the column number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR==1 {for (i=1; i&amp;lt;=NF; i++) col[$i]=i; next}
         {print $col["product"], $col["price"]}'&lt;/span&gt; sales.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pen 1.50
book 12.00
book 12.00
pen 1.50
pen 1.50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;next&lt;/code&gt; tells AWK to stop processing the header line and move on to the next one. If the file gains a new column, this command keeps working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take the column number from a shell variable
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;col&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="s1"&gt;'{print $col}'&lt;/span&gt; w.txt     &lt;span class="c"&gt;# c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the number comes from outside, one script can print a different column each time, for example &lt;code&gt;awk -v col="$1" '{print $col}' file.txt&lt;/code&gt; inside a shell script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unique values from a column
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 {print $2}'&lt;/span&gt; sales.csv | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;span class="c"&gt;# east&lt;/span&gt;
&lt;span class="c"&gt;# north&lt;/span&gt;
&lt;span class="c"&gt;# south&lt;/span&gt;

&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'NR&amp;gt;1 &amp;amp;&amp;amp; !seen[$2]++ {print $2}'&lt;/span&gt; sales.csv     &lt;span class="c"&gt;# keeps first-seen order&lt;/span&gt;
&lt;span class="c"&gt;# north&lt;/span&gt;
&lt;span class="c"&gt;# south&lt;/span&gt;
&lt;span class="c"&gt;# east&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problems and fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The column has spaces inside it
&lt;/h3&gt;

&lt;p&gt;With the default splitting, a tab-separated file where a name contains a space gives you half the name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'id\tname\tcity\n1\tAda Lovelace\tLondon\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $2}'&lt;/span&gt;
&lt;span class="c"&gt;# Ada&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'id\tname\tcity\n1\tAda Lovelace\tLondon\n'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $2}'&lt;/span&gt;
&lt;span class="c"&gt;# Ada Lovelace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the separator to a tab with &lt;code&gt;-F'\t'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsing &lt;code&gt;ls -l&lt;/code&gt; output
&lt;/h3&gt;

&lt;p&gt;A file name with a space is split into several fields, so &lt;code&gt;$9&lt;/code&gt; returns only the first word and &lt;code&gt;$NF&lt;/code&gt; returns only the last one. In my test, a file called &lt;code&gt;my report.txt&lt;/code&gt; gave &lt;code&gt;my&lt;/code&gt; for &lt;code&gt;$9&lt;/code&gt; and &lt;code&gt;report.txt&lt;/code&gt; for &lt;code&gt;$NF&lt;/code&gt;. Do not parse &lt;code&gt;ls&lt;/code&gt; output when names may contain spaces; use &lt;code&gt;find&lt;/code&gt; or a shell glob instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  A blank line crashes &lt;code&gt;$(NF-1)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;On an empty line &lt;code&gt;NF&lt;/code&gt; is 0, so &lt;code&gt;$(NF-1)&lt;/code&gt; asks for field -1. Both gawk and mawk stop with an error and exit code 2 (gawk says "attempt to access field -1", mawk says "negative field index $-1"). On a line with only one field, &lt;code&gt;$(NF-1)&lt;/code&gt; is &lt;code&gt;$0&lt;/code&gt;, the whole line. Guard it with a condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NF&amp;gt;1 {print $(NF-1)}'&lt;/span&gt; file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Files from Windows (CRLF line endings)
&lt;/h3&gt;

&lt;p&gt;Each line ends in a hidden carriage return, which becomes part of the last field. &lt;code&gt;od -c&lt;/code&gt; shows it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{print $NF "|"}'&lt;/span&gt; crlf.csv | &lt;span class="nb"&gt;od&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;
&lt;span class="c"&gt;# b  \r   |  \n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'{sub(/\r$/,""); print $NF "|"}'&lt;/span&gt; crlf.csv
&lt;span class="c"&gt;# b|&lt;/span&gt;
&lt;span class="c"&gt;# d|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CSV fields with commas inside quotes
&lt;/h3&gt;

&lt;p&gt;Splitting on &lt;code&gt;-F,&lt;/code&gt; cannot know that &lt;code&gt;"hi, there"&lt;/code&gt; is one value. On the line &lt;code&gt;Ada,"hi, there"&lt;/code&gt;, field 2 is &lt;code&gt;"hi&lt;/code&gt; and field 3 is &lt;code&gt;there"&lt;/code&gt;. For real CSV files you need a parser; this is covered in the CSV article of this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I print the last column in AWK?
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;$NF&lt;/code&gt;. For the second to last, use &lt;code&gt;$(NF-1)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I print columns 2 to 4?
&lt;/h3&gt;

&lt;p&gt;Use the range loop shown above, with &lt;code&gt;from=2&lt;/code&gt; and &lt;code&gt;to=4&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I print a column when the delimiter is a comma?
&lt;/h3&gt;

&lt;p&gt;Add &lt;code&gt;-F,&lt;/code&gt;, for example &lt;code&gt;awk -F, '{print $3}' file.csv&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between &lt;code&gt;print $1, $2&lt;/code&gt; and &lt;code&gt;print $1 $2&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;The comma puts the output separator (a space by default) between the values. Without it they are joined with nothing in between.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/awk-print-column" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>awk</category>
      <category>linux</category>
      <category>shell</category>
      <category>commandline</category>
    </item>
    <item>
      <title>PHP json_decode() Returns null: Catch Invalid JSON Errors</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Wed, 28 Oct 2026 15:00:04 +0000</pubDate>
      <link>https://dev.to/devtalk94/php-jsondecode-returns-null-catch-invalid-json-errors-5a1p</link>
      <guid>https://dev.to/devtalk94/php-jsondecode-returns-null-catch-invalid-json-errors-5a1p</guid>
      <description>&lt;p&gt;Here is a bug that looks like a data problem and is really an error-handling problem. A payment webhook arrives, your code decodes it, and the order never gets marked as paid. No exception, no log line, nothing. The cause is one function that fails quietly: &lt;code&gt;json_decode()&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt; when the JSON is broken, which is exactly what it also returns for a perfectly valid &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The silent failure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{"event":"paid","amount":1999,}'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// trailing comma: invalid JSON&lt;/span&gt;

&lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'event'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'none'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// prints: none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variable &lt;code&gt;$data&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt;, the &lt;code&gt;??&lt;/code&gt; operator hides the problem, and the script carries on as if the webhook was empty. Now compare with a valid payload:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;json_decode()&lt;/code&gt; returns&lt;/th&gt;
&lt;th&gt;Is it an error?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{"event":"paid",}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes (syntax error)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No, it is valid JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;''&lt;/code&gt; (empty string)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes (syntax error)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You cannot tell these apart from the return value alone. The old workaround is to call &lt;code&gt;json_last_error()&lt;/code&gt; straight afterwards, which does work (it returned &lt;code&gt;4&lt;/code&gt; for the broken payload and &lt;code&gt;0&lt;/code&gt; for &lt;code&gt;null&lt;/code&gt; when I ran it). But it relies on a global error state that you have to remember to check, in the right place, every single time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: JSON_THROW_ON_ERROR
&lt;/h2&gt;

&lt;p&gt;Pass the &lt;code&gt;JSON_THROW_ON_ERROR&lt;/code&gt; flag and PHP throws a &lt;code&gt;JsonException&lt;/code&gt; instead of returning &lt;code&gt;null&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// $e-&amp;gt;getMessage() === 'Syntax error'&lt;/span&gt;
    &lt;span class="c1"&gt;// $e-&amp;gt;getCode()    === 4   (the value of JSON_ERROR_SYNTAX)&lt;/span&gt;
    &lt;span class="nb"&gt;http_response_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Invalid JSON: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a broken payload stops at the &lt;code&gt;catch&lt;/code&gt; block with a reason, instead of travelling further into your code as &lt;code&gt;null&lt;/code&gt;. The exception message and code are the same ones &lt;code&gt;json_last_error_msg()&lt;/code&gt; and &lt;code&gt;json_last_error()&lt;/code&gt; would give you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The named-argument shortcut
&lt;/h2&gt;

&lt;p&gt;The signature of the function is &lt;code&gt;json_decode(string $json, ?bool $associative = null, int $depth = 512, int $flags = 0)&lt;/code&gt;. The flag is the fourth parameter, so the positional version forces you to type the default depth just to reach it: &lt;code&gt;512&lt;/code&gt;. With a named argument you can skip it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This needs PHP 8.0 or newer, because that is when named arguments arrived. I checked that it behaves the same as the positional call, and that a broken payload throws with the same message.&lt;/p&gt;

&lt;p&gt;One more detail: the second parameter is optional too. If you leave out &lt;code&gt;true&lt;/code&gt;, you get objects instead of arrays, and you can still ask for arrays through a flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                          &lt;span class="c1"&gt;// stdClass object&lt;/span&gt;
&lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;JSON_OBJECT_AS_ARRAY&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Things that still catch people out
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A valid&lt;/strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;strong&gt;is still&lt;/strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;strong&gt;.&lt;/strong&gt; The flag only throws on errors, and &lt;code&gt;null&lt;/code&gt; is not an error. If you expect an object or an array, check the type after decoding: &lt;code&gt;if (!is_array($data)) { ... }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An empty string throws.&lt;/strong&gt; &lt;code&gt;json_decode('', true, flags: JSON_THROW_ON_ERROR)&lt;/code&gt; gives a syntax error, so an empty request body ends up in your &lt;code&gt;catch&lt;/code&gt; block too. Usually that is what you want.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The flag leaves&lt;/strong&gt;&lt;code&gt;json_last_error()&lt;/code&gt;&lt;strong&gt;alone.&lt;/strong&gt; In my test, a call with the flag neither set nor reset the global error state. Do not mix the two styles in the same code path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depth is an error as well.&lt;/strong&gt; &lt;code&gt;json_decode('[[1]]', true, 1, JSON_THROW_ON_ERROR)&lt;/code&gt; throws "Maximum stack depth exceeded".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;json_encode()&lt;/code&gt;&lt;strong&gt;has the same problem and the same fix.&lt;/strong&gt; Without the flag, &lt;code&gt;json_encode("\xB1\x31")&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;. With it, you get a &lt;code&gt;JsonException&lt;/code&gt; saying "Malformed UTF-8 characters, possibly incorrectly encoded".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Only need to know if it is valid?
&lt;/h2&gt;

&lt;p&gt;From PHP 8.3 there is &lt;code&gt;json_validate()&lt;/code&gt;, which returns a boolean without building the decoded data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;json_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'{"a":1}'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nb"&gt;json_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'{"a":1,}'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nb"&gt;json_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'null'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it when you only want to check a string, for example before storing it. If you need the data, decode once with &lt;code&gt;JSON_THROW_ON_ERROR&lt;/code&gt; and skip the extra pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  PHP 8.6 makes the message more useful
&lt;/h2&gt;

&lt;p&gt;On PHP 8.6.0 RC4 (a release candidate, so details may still change), the same broken payload produced &lt;code&gt;Syntax error near location 1:16&lt;/code&gt; instead of plain &lt;code&gt;Syntax error&lt;/code&gt;, and the depth error added a location too. On PHP 8.3.6 and 8.4.27 the messages were the short ones. If you log or test against exact message text, expect it to change when you upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  A handy rule
&lt;/h2&gt;

&lt;p&gt;If the JSON comes from outside your code (a request body, a webhook, a queue message, an API response), decode it with &lt;code&gt;JSON_THROW_ON_ERROR&lt;/code&gt;. If you wrote the string yourself a line earlier, plain &lt;code&gt;json_decode()&lt;/code&gt; is fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.php.net/manual/en/function.json-decode.php" rel="noopener noreferrer"&gt;PHP manual: json_decode()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.php.net/manual/en/class.jsonexception.php" rel="noopener noreferrer"&gt;PHP manual: JsonException&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.php.net/manual/en/function.json-validate.php" rel="noopener noreferrer"&gt;PHP manual: json_validate()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/php-json-decode-null-invalid-json" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>json</category>
      <category>jsondecode</category>
      <category>jsonexception</category>
    </item>
    <item>
      <title>Node.js October 2026 Security Release: What to Patch Now</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Wed, 28 Oct 2026 14:02:07 +0000</pubDate>
      <link>https://dev.to/devtalk94/nodejs-october-2026-security-release-what-to-patch-now-467k</link>
      <guid>https://dev.to/devtalk94/nodejs-october-2026-security-release-what-to-patch-now-467k</guid>
      <description>&lt;p&gt;On Wednesday, October 21, 2026, the Node.js project shipped security releases for three lines: 26.x, 24.x and 22.x. The advisory fixes 10 vulnerabilities, 4 rated High, 3 Medium and 3 Low. One of the High ones, a memory-corruption bug in the bundled Brotli decoder, is described as allowing remote code execution, and that is the reason to patch this week rather than at the next maintenance window.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Release line&lt;/th&gt;
&lt;th&gt;Upgrade to at least&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node.js 26.x&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v26.12.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js 24.x&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v24.22.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js 22.x&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v22.23.4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The advisory lists no workarounds for any of the 10 issues, so upgrading is the fix. It also says end-of-life versions are always affected when a security release ships. Node.js 20 and earlier are end of life and get no patch, so on those the only route is moving to a supported line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check what you are running
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# on a server or CI runner&lt;/span&gt;
node &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# inside a container image&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; your-image:tag node &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# are you on Express 5 / body-parser 2?&lt;/span&gt;
npm &lt;span class="nb"&gt;ls &lt;/span&gt;express body-parser

&lt;span class="c"&gt;# do you call the Brotli API directly?&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"brotliDecompress&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;createBrotliDecompress"&lt;/span&gt; src/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An image pinned to an old tag stays vulnerable until you rebuild it. After upgrading, restart every Node.js process, containers included, so the patched runtime is actually loaded. If you use your distribution's Node.js package, also check your distro's advisory: the Aikido write-up notes that packaged builds may use the system Brotli library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# nvm example&lt;/span&gt;
nvm &lt;span class="nb"&gt;install &lt;/span&gt;24
nvm &lt;span class="nb"&gt;alias &lt;/span&gt;default 24
node &lt;span class="nt"&gt;--version&lt;/span&gt;     &lt;span class="c"&gt;# expect v24.22.1 or newer&lt;/span&gt;

&lt;span class="c"&gt;# Docker: rebuild from a fresh base image&lt;/span&gt;
docker build &lt;span class="nt"&gt;--pull&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; my-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  All 10 vulnerabilities
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CVE&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Lines&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-111497&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;26, 24, 22&lt;/td&gt;
&lt;td&gt;Brotli decoder out-of-bounds write in &lt;code&gt;node:zlib&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-105482&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;26, 24, 22&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dns.resolve*()&lt;/code&gt; truncates a hostname at a NUL character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-105483&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;26, 24&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;tls.checkServerIdentity()&lt;/code&gt; fails to treat an IP written with fullwidth digits as an IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-106069&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;26, 24, 22&lt;/td&gt;
&lt;td&gt;TLS hostname check bypass when a host "loosely" resolves to an IP address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-105481&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;26, 24, 22&lt;/td&gt;
&lt;td&gt;Keep-alive socket treats data that arrived early as the response to the next request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-106072&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;26, 24, 22&lt;/td&gt;
&lt;td&gt;DNS names in a certificate can authenticate non-canonical IPv4 spellings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-106074&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;26, 24, 22&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;crypto.randomUUID()&lt;/code&gt; and &lt;code&gt;randomInt()&lt;/code&gt; caches saved into snapshots and single executable apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-106070&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;26 only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;zlib.ZipFile&lt;/code&gt; returns attacker-controlled bytes after a failed header validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-106071&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;26 only&lt;/td&gt;
&lt;td&gt;Permission Model wildcard grant allows more than intended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-106073&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;26, 24, 22&lt;/td&gt;
&lt;td&gt;Permission Model bypass through &lt;code&gt;fs.realpathSync()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The release also updates c-ares, undici, the CA certificate bundle (the advisory lists "eMudhra Intermediate CA Compromise"), OpenSSL on 24.x and 22.x, and SQLite on 22.x.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Brotli bug is the urgent one
&lt;/h2&gt;

&lt;p&gt;CVE-2026-111497 is an out-of-bounds write in the Brotli decoder that Node.js bundles. According to the advisory, an invalid block switch was treated as "need more input" instead of a hard format error, so the streaming decoder kept appending data to a small internal buffer. Malicious Brotli data passed to &lt;code&gt;node:zlib&lt;/code&gt; decompression can cause a crash, disclose process memory, or lead to remote code execution.&lt;/p&gt;

&lt;p&gt;The question that decides your exposure is whether untrusted Brotli data ever reaches your process. I checked the common paths on Node.js 22.22.0, using harmless data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fetch()&lt;/code&gt;&lt;strong&gt;decodes Brotli responses on its own.&lt;/strong&gt; A test server answering with &lt;code&gt;Content-Encoding: br&lt;/code&gt; came back as plain text. So any server your code fetches from, including a user-supplied webhook or URL, can send your app Brotli data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A bare&lt;/strong&gt;&lt;code&gt;http&lt;/code&gt;&lt;strong&gt;server does not decode request bodies.&lt;/strong&gt; My test server saw the raw compressed bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express 5 and body-parser 2 do.&lt;/strong&gt; An Express 5.3.0 app using &lt;code&gt;express.json()&lt;/code&gt; (with &lt;code&gt;body-parser&lt;/code&gt; 2.4.0, which has a &lt;code&gt;br&lt;/code&gt; case in its body reader) parsed a Brotli-compressed JSON body without any extra setup. So any client can feed Brotli data to such an app. That matches what Aikido reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short: if you run Express 5 or call &lt;code&gt;fetch()&lt;/code&gt; against servers you do not control, assume you are exposed.&lt;/p&gt;

&lt;h3&gt;
  
  
  How bad is it in practice?
&lt;/h3&gt;

&lt;p&gt;Public reports disagree on how easily it can be exploited, so I will not over-claim either way. The Hacker News says a public proof of concept appeared on October 22 and reached code execution only against a deliberately weakened test build, with a crash and a limited memory read on a standard build. Aikido reports later code execution against an unmodified Node.js 24.22.0 image on Linux arm64, not on every attempt, and says 24.22.1 rejects the same input. Both say no exploitation in the wild had been reported as of October 22, and Qualys notes the CVE records did not yet have CVSS scores. A memory-safety flaw in a runtime that handles untrusted network input, with a public proof of concept and a routine version bump as the fix, is a good reason to patch now.&lt;/p&gt;

&lt;h3&gt;
  
  
  A stopgap if you cannot upgrade today
&lt;/h3&gt;

&lt;p&gt;It does not replace the upgrade, and it does not cover Brotli responses your app fetches from other servers. But for an Express 5 API you can refuse Brotli request bodies before any body parser runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;br&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-encoding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;415&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unsupported Content-Encoding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// must come after the check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran this with Express 5.3.0: a plain JSON body and a gzip body returned 200, and a Brotli body returned 415. The same rule can also live at your proxy or load balancer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The TLS hostname bugs: real, but narrower
&lt;/h2&gt;

&lt;p&gt;Three of the High and Medium issues (CVE-2026-105483, CVE-2026-106069, CVE-2026-106072) are in how &lt;code&gt;tls.checkServerIdentity()&lt;/code&gt; decides that a certificate matches the host you connected to. The advisory describes them as IP-address edge cases: an IP written in an unusual way (fullwidth digits, leading zeros, hex or a bare integer) gets matched against DNS names in the certificate instead of its IP entries. For CVE-2026-106072 the advisory says exploitation needs a numeric DNS name in a certificate from a CA the client trusts, plus a resolver that accepts those spellings, such as glibc.&lt;/p&gt;

&lt;p&gt;The leading-zero case rests on a real parsing difference. WHATWG URL parsing reads &lt;code&gt;0127&lt;/code&gt; as octal, and some operating system resolvers read it as decimal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://0127.0.0.1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;   &lt;span class="c1"&gt;// '87.0.0.1'  (checked on Node 22.22.0)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the certificate can be validated against a different address than the one the socket connects to. The practical takeaway: if your services connect to other services by IP address, or take hostnames or URLs from users, these matter. If you only connect to normal DNS names, your exposure is much smaller, but the patch is still worth taking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The DNS bug: validate, then resolve
&lt;/h2&gt;

&lt;p&gt;CVE-2026-105482 means &lt;code&gt;dns.resolve*()&lt;/code&gt; and &lt;code&gt;dns.reverse()&lt;/code&gt; cut a name off at an embedded NUL byte, because the name is handed to c-ares as a C string. The advisory's example: &lt;code&gt;attacker.example\0.trusted.example&lt;/code&gt; resolves &lt;code&gt;attacker.example&lt;/code&gt;. The risky pattern is checking a hostname against an allowlist and then resolving it, which is common in SSRF protection code. The advisory calls it an incomplete fix of CVE-2026-48930, which covered only &lt;code&gt;dns.lookup()&lt;/code&gt; and the &lt;code&gt;net&lt;/code&gt;/&lt;code&gt;tls&lt;/code&gt; host options. The fix is the upgrade. As defence in depth (my suggestion, not from the advisory), reject names containing a NUL byte before you validate them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid hostname&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The rest, and who can skip them
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep-alive socket (CVE-2026-105481):&lt;/strong&gt; unsolicited data that arrived on an idle reused socket can be parsed as the response to your next request. It is described as an incomplete fix of CVE-2026-48931.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;randomUUID()&lt;/code&gt;&lt;strong&gt;/&lt;/strong&gt;&lt;code&gt;randomInt()&lt;/code&gt;&lt;strong&gt;(CVE-2026-106074):&lt;/strong&gt; only matters if you build user-land startup snapshots (&lt;code&gt;--build-snapshot&lt;/code&gt;) or single executable apps with &lt;code&gt;useSnapshot&lt;/code&gt;. Every process started from the snapshot returns the same values until the cache refills, so IDs can collide or be predicted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission Model (CVE-2026-106071, CVE-2026-106073):&lt;/strong&gt; only matters if you run with &lt;code&gt;--permission&lt;/code&gt; and &lt;code&gt;--allow-fs-read&lt;/code&gt; or &lt;code&gt;--allow-fs-write&lt;/code&gt;. 106071 is on 26.x only.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zlib.ZipFile&lt;/code&gt;&lt;strong&gt;(CVE-2026-106070):&lt;/strong&gt; 26.x only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is this being exploited?
&lt;/h3&gt;

&lt;p&gt;As of October 22, the sources I read reported no exploitation in the wild. A public proof of concept exists, so do not treat that as permission to wait.&lt;/p&gt;

&lt;h3&gt;
  
  
  I don't use Brotli. Am I safe?
&lt;/h3&gt;

&lt;p&gt;Not necessarily. Brotli decoding can happen without you calling it: through &lt;code&gt;fetch()&lt;/code&gt; responses, or through Express 5 request body parsing, as shown above. And the other nine fixes apply regardless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I stay on Node.js 20?
&lt;/h3&gt;

&lt;p&gt;It is end of life and will not receive these fixes. Plan the move to a supported line.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/blog/vulnerability/october-2026-security-releases" rel="noopener noreferrer"&gt;Node.js: Wednesday, October 21, 2026 Security Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thehackernews.com/2026/10/nodejs-brotli-decoder-bug-could-allow.html" rel="noopener noreferrer"&gt;The Hacker News: Node.js Brotli decoder bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.qualys.com/product-tech/2026/10/23/node-js-patches-brotli-remote-code-execution-flaw-find-vulnerable-runtimes-before-attackers-do" rel="noopener noreferrer"&gt;Qualys: Node.js patches Brotli remote code execution flaw&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aikido.dev/blog/node2shell-nodejs-brotli-rce-cve-2026-111497" rel="noopener noreferrer"&gt;Aikido: Node.js Brotli vulnerability and the fix&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/nodejs-october-2026-security-release" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>security</category>
      <category>cve</category>
      <category>brotli</category>
    </item>
    <item>
      <title>PHP 8.2 End of Life: Dates, Risks and How to Upgrade</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Wed, 28 Oct 2026 12:17:26 +0000</pubDate>
      <link>https://dev.to/devtalk94/php-82-end-of-life-dates-risks-and-how-to-upgrade-5gi4</link>
      <guid>https://dev.to/devtalk94/php-82-end-of-life-dates-risks-and-how-to-upgrade-5gi4</guid>
      <description>&lt;p&gt;PHP 8.2 stops receiving security fixes on 31 December 2026. Your site will not break on 1 January. What changes is that the next serious PHP vulnerability will be fixed in newer versions only, and 8.2 will stay exposed. If you still run 8.2 in production, you have about two months. That is enough time to upgrade properly, as long as you start by finding out what is blocking you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dates
&lt;/h2&gt;

&lt;p&gt;PHP gives each release branch two years of active support (bug fixes and security fixes), then two more years of security fixes only. After that the branch reaches end of life. This is the table from php.net:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Released&lt;/th&gt;
&lt;th&gt;Active support ended / ends&lt;/th&gt;
&lt;th&gt;Security support ends&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;8.2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8 Dec 2022&lt;/td&gt;
&lt;td&gt;31 Dec 2024&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31 Dec 2026&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.3&lt;/td&gt;
&lt;td&gt;23 Nov 2023&lt;/td&gt;
&lt;td&gt;31 Dec 2025&lt;/td&gt;
&lt;td&gt;31 Dec 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.4&lt;/td&gt;
&lt;td&gt;21 Nov 2024&lt;/td&gt;
&lt;td&gt;31 Dec 2026&lt;/td&gt;
&lt;td&gt;31 Dec 2028&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.5&lt;/td&gt;
&lt;td&gt;20 Nov 2025&lt;/td&gt;
&lt;td&gt;31 Dec 2027&lt;/td&gt;
&lt;td&gt;31 Dec 2029&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note the second column for 8.2: it already lost active support in December 2024. For the past two years it has received security fixes only, and in two months it will receive nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What end of life means in practice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No more patches for new vulnerabilities.&lt;/strong&gt; The latest 8.2 release at the time of writing is 8.2.35. After the end-of-life date, the PHP project stops issuing security fixes for the branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your host may move before the deadline.&lt;/strong&gt; Acquia, for example, set its own cutoff at 1 October 2026 and said it would upgrade remaining PHP 8.2 applications to 8.4 itself. Check your provider's notices, because a forced upgrade you did not test is worse than one you planned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audits and scanners will flag it.&lt;/strong&gt; Security scanners and compliance reviews usually treat an end-of-life runtime as a finding by itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which version to move to
&lt;/h2&gt;

&lt;p&gt;The smallest hop is 8.3, but it only buys you one more year (security fixes until 31 December 2027). If you are going to do the work anyway, aim higher:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PHP 8.4&lt;/strong&gt; is a good default. Security support runs to the end of 2028.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP 8.5&lt;/strong&gt; gives the longest runway (end of 2029). Choose it if your framework and dependencies already support it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP 8.3&lt;/strong&gt; makes sense only as a stepping stone when something blocks you from going further.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use Laravel, look at the &lt;code&gt;php&lt;/code&gt; constraint in the framework's own &lt;code&gt;composer.json&lt;/code&gt;. On the branch heads I checked, Laravel 13.x requires &lt;code&gt;^8.3&lt;/code&gt;, while 12.x and 11.x require &lt;code&gt;^8.2&lt;/code&gt;. So on Laravel 12 you can upgrade PHP first and Laravel later, but a move to Laravel 13 already means leaving 8.2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: find every place that runs 8.2
&lt;/h2&gt;

&lt;p&gt;Production is the obvious one. The forgotten ones are CI images, queue workers, cron containers and staging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-v&lt;/span&gt;                                   &lt;span class="c"&gt;# on every server and container&lt;/span&gt;

&lt;span class="c"&gt;# what your repo pins&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'"php"'&lt;/span&gt; composer.json
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"php:8.2&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;php8.2&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;8&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;2"&lt;/span&gt; Dockerfile&lt;span class="k"&gt;*&lt;/span&gt; docker-compose&lt;span class="k"&gt;*&lt;/span&gt; .github 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: let Composer tell you what is blocking you
&lt;/h2&gt;

&lt;p&gt;Before changing anything, ask Composer which package would stop you from running a newer PHP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer why-not php 8.4.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tested this with a package that declares &lt;code&gt;"php": "~8.2.0"&lt;/code&gt;, and the output points straight at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acme/old-lib 1.0.0 requires php (~8.2.0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line is a package you must upgrade, replace, or ask the maintainer about. Two details matter. If your &lt;code&gt;composer.json&lt;/code&gt; sets &lt;code&gt;config.platform.php&lt;/code&gt; to fake an older PHP, remember to raise or remove it when you upgrade. And &lt;code&gt;composer check-platform-reqs&lt;/code&gt; checks the PHP that is actually running, so run it on the new version, inside the new environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: catch deprecations without running the app
&lt;/h2&gt;

&lt;p&gt;The cheapest test is to lint your whole codebase with the new PHP. Linting compiles each file, and some deprecations are raised at compile time. The most common one when moving to 8.4 is the implicit nullable parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On PHP 8.4.27 a lint run reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deprecated: send(): Implicitly marking parameter $options as nullable is deprecated, the explicit nullable type must be used instead in nullable.php on line 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PHP 8.3.6 printed nothing for the same file, which is why this appears only after you move to 8.4. The fix is to add the question mark:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;?array&lt;/span&gt; &lt;span class="nv"&gt;$options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check a whole project with the new PHP binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find app src &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.php'&lt;/span&gt; &lt;span class="nt"&gt;-print0&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | xargs &lt;span class="nt"&gt;-0&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; php &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;error_reporting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;E_ALL &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;display_errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;-l&lt;/span&gt; 2&amp;gt;&amp;amp;1 &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"No syntax errors"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A second one is calling &lt;code&gt;get_class()&lt;/code&gt; with no arguments inside a class. Both PHP 8.3.6 and 8.4.27 reported &lt;code&gt;Calling get_class() without arguments is deprecated&lt;/code&gt;. Use &lt;code&gt;self::class&lt;/code&gt;, &lt;code&gt;static::class&lt;/code&gt; or &lt;code&gt;get_class($this)&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Linting only finds what the compiler sees. Deprecations that happen when code runs will appear only if that code path executes, so also run your full test suite on the new PHP version with &lt;code&gt;error_reporting&lt;/code&gt; set to &lt;code&gt;E_ALL&lt;/code&gt;, and read the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: switch the runtime and roll out
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Update the Docker base image, for example &lt;code&gt;FROM php:8.2-cli&lt;/code&gt; to &lt;code&gt;FROM php:8.4-cli&lt;/code&gt;, and rebuild with &lt;code&gt;docker build --pull&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the new version to your CI matrix first, and make it pass before touching production.&lt;/li&gt;
&lt;li&gt;Check that every PHP extension you need is available for the new version, including ones you install separately. If you build extensions yourself, see the &lt;a href="https://dev-talk.com/php-pie-install-php-extensions" rel="noopener noreferrer"&gt;PIE installation guide&lt;/a&gt;. Xdebug, for instance, has to be a release that supports your PHP version.&lt;/li&gt;
&lt;li&gt;Deploy to staging, then to one production node, then everywhere. Keep the old image tag so you can roll back.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Will my site stop working on 1 January 2027?
&lt;/h3&gt;

&lt;p&gt;No. PHP 8.2 keeps running. You just stop getting security fixes from the PHP project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is PHP 8.3 a safe place to land?
&lt;/h3&gt;

&lt;p&gt;It is supported until 31 December 2027, so it is safe for now, but you would be repeating this exercise in a year. If 8.3 is the most your dependencies allow today, use it, and plan the next hop.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Linux distribution still ships PHP 8.2. Am I covered?
&lt;/h3&gt;

&lt;p&gt;That depends on your distribution's own support policy, not on php.net. Check your vendor's security notices for the exact package version before assuming you are covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.php.net/supported-versions.php" rel="noopener noreferrer"&gt;PHP: Supported Versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://php.watch/versions/8.4/implicitly-marking-parameter-type-nullable-deprecated" rel="noopener noreferrer"&gt;PHP.Watch: Implicitly nullable parameter declarations deprecated in PHP 8.4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.acquia.com/acquia-cloud-platform/end-life-php-82-favor-php-84-faq" rel="noopener noreferrer"&gt;Acquia: End of life for PHP 8.2 FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://getcomposer.org/doc/03-cli.md" rel="noopener noreferrer"&gt;Composer: command-line interface (why-not, check-platform-reqs)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/php-8-2-end-of-life" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>php82</category>
      <category>endoflife</category>
      <category>php84</category>
    </item>
    <item>
      <title>PHP 8.6 Release Date, New Features and Deprecations</title>
      <dc:creator>devTalk</dc:creator>
      <pubDate>Mon, 26 Oct 2026 02:54:22 +0000</pubDate>
      <link>https://dev.to/devtalk94/php-86-release-date-new-features-and-deprecations-38jn</link>
      <guid>https://dev.to/devtalk94/php-86-release-date-new-features-and-deprecations-38jn</guid>
      <description>&lt;p&gt;PHP 8.6 is scheduled for general availability on &lt;strong&gt;19 November 2026&lt;/strong&gt;. The feature freeze has passed, release candidate 4 is out, and the headline features are known: partial function application, a &lt;code&gt;clamp()&lt;/code&gt; function, a proper &lt;code&gt;Duration&lt;/code&gt; class, and a batch of deprecations that may light up your logs. I built PHP 8.6.0 RC4 from the official source and ran every example below, so you can see what the code does today instead of what a blog post promises. One warning first: it is a release candidate, and it is not for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dates
&lt;/h2&gt;

&lt;p&gt;From the schedule on wiki.php.net: RC4 shipped on 21 October, RC5 is planned for 5 November, and GA is planned for 19 November 2026. The hard feature freeze was 22 September, so new features should not appear now, but dates and small details can still move. PHP 8.2 reaches end of life on 31 December 2026, which makes this a good moment to plan your upgrade path anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Partial function application: the ? placeholder
&lt;/h2&gt;

&lt;p&gt;You can now create a function from another function by leaving some arguments open with &lt;code&gt;?&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$slug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello World'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// "Hello-World"  ($slug is a Closure)&lt;/span&gt;

&lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str_repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ab'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;   &lt;span class="c1"&gt;// ["abab", "cc"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works on methods too (&lt;code&gt;$greeter-&amp;gt;greet('Hello', ?)&lt;/code&gt; gave "Hello, Ada!"), and with several placeholders (&lt;code&gt;array_slice(?, ?, 2)&lt;/code&gt; called with &lt;code&gt;[1,2,3,4,5]&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; returned &lt;code&gt;[2,3]&lt;/code&gt;). Use &lt;code&gt;...&lt;/code&gt; to leave all remaining arguments open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$pad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'7'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;STR_PAD_LEFT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// "007"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things I hit while testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;...&lt;/code&gt;&lt;strong&gt;must come last.&lt;/strong&gt; Writing &lt;code&gt;str_pad(..., pad_type: STR_PAD_LEFT)&lt;/code&gt; is a fatal error: "Variadic placeholder must be last".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every&lt;/strong&gt;&lt;code&gt;?&lt;/code&gt;&lt;strong&gt;becomes a required parameter,&lt;/strong&gt; even if the original parameter was optional. Calling a closure made with &lt;code&gt;str_replace(' ', '-', ?, ?)&lt;/code&gt; with one argument threw &lt;code&gt;ArgumentCountError&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Named arguments work alongside placeholders: &lt;code&gt;str_pad(?, 3, pad_type: STR_PAD_LEFT, pad_string: '0')&lt;/code&gt; then called with &lt;code&gt;'7'&lt;/code&gt; gave &lt;code&gt;007&lt;/code&gt;. According to the upgrade notes, &lt;code&gt;array_map()&lt;/code&gt; with a partial application or first-class callable is compiled into a plain loop, which avoids creating extra closures. I did not benchmark that claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. clamp()
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 100&lt;/span&gt;
&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;              &lt;span class="c1"&gt;// 0&lt;/span&gt;
&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;              &lt;span class="c1"&gt;// 42&lt;/span&gt;
&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                &lt;span class="c1"&gt;// ValueError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error message was: "clamp(): Argument #2 ($min) must be smaller than or equal to argument #3 ($max)". No more hand-written &lt;code&gt;max($min, min($max, $value))&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Time\Duration
&lt;/h2&gt;

&lt;p&gt;An immutable class for lengths of time. It has factory methods (&lt;code&gt;fromSeconds&lt;/code&gt;, &lt;code&gt;fromMilliseconds&lt;/code&gt;, &lt;code&gt;fromMinutes&lt;/code&gt;, &lt;code&gt;fromHours&lt;/code&gt;, and &lt;code&gt;fromIso8601DurationString&lt;/code&gt;), arithmetic (&lt;code&gt;add&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;multiplyBy&lt;/code&gt;, &lt;code&gt;divideBy&lt;/code&gt;, &lt;code&gt;negate&lt;/code&gt;, &lt;code&gt;absolute&lt;/code&gt;) and a static &lt;code&gt;compare()&lt;/code&gt;. The readable properties are &lt;code&gt;seconds&lt;/code&gt;, &lt;code&gt;nanoseconds&lt;/code&gt; and &lt;code&gt;negative&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Time\Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// exponential backoff: 100ms, 200ms, 400ms, 800ms&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;multiplyBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nv"&gt;$attempt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                        &lt;span class="c1"&gt;// 5400&lt;/span&gt;
&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromIso8601DurationString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PT1H30M'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;                                                          &lt;span class="c1"&gt;// 0 (equal)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backoff loop produced 100,000,000, 200,000,000, 400,000,000 and 800,000,000 nanoseconds. Trying to assign &lt;code&gt;$duration-&amp;gt;seconds = 9&lt;/code&gt; threw "Cannot modify readonly property". Note that the class has no &lt;code&gt;toMilliseconds()&lt;/code&gt; method in RC4: you read &lt;code&gt;seconds&lt;/code&gt; and &lt;code&gt;nanoseconds&lt;/code&gt; yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Smaller additions that worked in my tests
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Readonly properties can have defaults:&lt;/strong&gt; &lt;code&gt;public readonly array $tags = ['a'];&lt;/code&gt; is now valid, and changing it still throws.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enums can define&lt;/strong&gt;&lt;code&gt;__debugInfo()&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt; &lt;code&gt;var_dump(Level::Low)&lt;/code&gt; printed my custom label and value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#[\Override]&lt;/code&gt;&lt;strong&gt;works on class constants.&lt;/strong&gt; On a constant that does not exist in a parent or interface, PHP stops with: "Lone::X has #[\Override] attribute, but no matching parent constant exists".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Objects stored in constants can be mutated:&lt;/strong&gt; after &lt;code&gt;const BOX = new Box;&lt;/code&gt;, &lt;code&gt;BOX-&amp;gt;n = 5;&lt;/code&gt; worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON errors say where:&lt;/strong&gt; &lt;code&gt;json_last_error_msg()&lt;/code&gt; returned "Syntax error near location 1:15", and &lt;code&gt;JsonException&lt;/code&gt; carries the same detail.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pack()&lt;/code&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;code&gt;unpack()&lt;/code&gt;&lt;strong&gt;accept&lt;/strong&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;strong&gt;and&lt;/strong&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; on signed integers: &lt;code&gt;pack('l&amp;gt;', -2)&lt;/code&gt; gave &lt;code&gt;fffffffe&lt;/code&gt; and &lt;code&gt;pack('l&amp;lt;', -2)&lt;/code&gt; gave &lt;code&gt;feffffff&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New in core:&lt;/strong&gt; a &lt;code&gt;SortDirection&lt;/code&gt; enum (cases &lt;code&gt;Ascending&lt;/code&gt; and &lt;code&gt;Descending&lt;/code&gt;) and a polling API (&lt;code&gt;Io\Poll&lt;/code&gt;). The upgrade notes list both; I confirmed the enum exists and did not exercise the polling API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deprecations: run your test suite with E_ALL
&lt;/h2&gt;

&lt;p&gt;I triggered each of these on RC4. They are deprecations, not removals, but they will add noise to logs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;What PHP 8.6 says&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;return&lt;/code&gt; inside &lt;code&gt;finally&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"Returning from a finally block is deprecated"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;return 5;&lt;/code&gt; in &lt;code&gt;__construct()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"Returning a value from a constructor is deprecated"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;define('X', 1, true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Third argument deprecated (it also emitted a warning that this will be an error in PHP 9.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A class named &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;is&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"Using "let" as a class name is deprecated since 8.6"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const _ = 1;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Calling a constant "_" is deprecated since 8.6"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Class constant named &lt;code&gt;namespace&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"Declaring class constant called 'namespace' is deprecated"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A function named &lt;code&gt;readonly&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"Calling a function "readonly" is deprecated since 8.6"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;is_a($str, $cls, false)&lt;/code&gt; with a string&lt;/td&gt;
&lt;td&gt;Deprecated when &lt;code&gt;$allow_string&lt;/code&gt; is false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__debugInfo(): ?array&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deprecated: declare a non-nullable &lt;code&gt;array&lt;/code&gt; return type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The upgrade notes list more, including Mbregex (the Oniguruma library behind it is no longer maintained) and several &lt;code&gt;mysqli&lt;/code&gt; and Reflection calls. Two that older codebases may hit are &lt;code&gt;return&lt;/code&gt; inside &lt;code&gt;finally&lt;/code&gt; and a third argument to &lt;code&gt;define()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changes that affect behavior
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safer session defaults.&lt;/strong&gt; &lt;code&gt;session.use_strict_mode&lt;/code&gt; now defaults to 1, &lt;code&gt;session.cookie_httponly&lt;/code&gt; to 1, and &lt;code&gt;session.cookie_samesite&lt;/code&gt; to "Lax". I confirmed the new defaults in the source. If your app reads the session cookie from JavaScript, or depends on cross-site POSTs carrying it, set the old values explicitly. Laravel manages its own session cookie settings, so most Laravel apps are unaffected; apps on native PHP sessions should check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;By-reference&lt;/strong&gt;&lt;code&gt;foreach&lt;/code&gt; can now visit elements it previously skipped after array compaction, and &lt;code&gt;??&lt;/code&gt; or &lt;code&gt;empty()&lt;/code&gt; on a magic property no longer calls &lt;code&gt;__get()&lt;/code&gt; in one specific case. Both are in the upgrade notes under backward-incompatible changes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;preg_grep()&lt;/code&gt;&lt;strong&gt;now returns&lt;/strong&gt;&lt;code&gt;false&lt;/code&gt; instead of a partial array when a PCRE error occurs, like the other &lt;code&gt;preg_*&lt;/code&gt; functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is Laravel ready?
&lt;/h2&gt;

&lt;p&gt;At the 13.38.0 tag, Laravel's &lt;code&gt;composer.json&lt;/code&gt; requires &lt;code&gt;^8.3&lt;/code&gt;, which allows PHP 8.6, but its test workflow covers PHP 8.3, 8.4 and 8.5, with no 8.6 in the list. So the framework is not yet tested against 8.6 in that release, and your packages probably are not either. A sensible plan: add an optional CI job on the 8.6 release candidate that is allowed to fail, run it with &lt;code&gt;error_reporting&lt;/code&gt; set to &lt;code&gt;E_ALL&lt;/code&gt;, and fix the deprecations that appear. Wait for your dependencies before moving production.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.php.net/todo/php86" rel="noopener noreferrer"&gt;PHP wiki: PHP 8.6 release schedule&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/php/php-src/blob/php-8.6.0RC4/UPGRADING" rel="noopener noreferrer"&gt;php-src: UPGRADING for PHP 8.6.0 RC4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://laravel-news.com/php-8-6" rel="noopener noreferrer"&gt;Laravel News: PHP 8.6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.php.net/" rel="noopener noreferrer"&gt;PHP.net: news and release announcements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://dev-talk.com/post/php-8-6-release-date-new-features" rel="noopener noreferrer"&gt;DEV Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>newfeatures</category>
      <category>deprecations</category>
      <category>partialfunctionapplication</category>
    </item>
  </channel>
</rss>
