In Ruby's command line, what do the -e, -n, -p, -l and -a flags do in a one-liner over a log file?
answer
- -e takes code, repeatable
- -n wraps code in a gets loop
- -p also prints $_ each pass
- -l chops lines, -a fills $F
- BEGIN and END run once
basics
~20 s-e runs code given on the command line; -n wraps it in a while gets loop with each line in $; -p does the same and prints $ after every pass; -l chops line endings and sets $; -a splits each line into $F.
solid answer
~40 s`ruby -e 'code'` runs code without a file, and `-e` may repeat. `-n` wraps that code in `while gets ... end`, so it runs once per line of the files named after it (or of standard input) with the line in `$_`. `-p` is `-n` plus printing `$_` at the end of each pass, which turns the code into a filter that may rewrite the line. `-l` chops each line and sets the output record separator `$\` to `$/`, and `-a` splits `$_` into the array `$F` (the separator can be set with `-F`). With `-n` or `-p`, Ruby also defines `Kernel#sub`, `gsub`, `chop` and `chomp` acting on `$_`, and `BEGIN { }` / `END { }` blocks give one-time setup and a final report.
code
bash · 5 lines# count ERROR lines across rotated logs
ruby -ne 'BEGIN { n = 0 }; n += 1 if $_.include?("ERROR"); END { puts n }' app.log app.log.1
# in-place edit, keeping app.log.bak
ruby -i.bak -pe 'gsub(/token=\S+/, "token=[FILTERED]")' app.loggo deeper
Recall that -e runs inline code and that -n loops over input lines while -p loops and prints. Know that the current line lives in $_.
Explain -l, -a with $F and -F, the loop-only sub and gsub helpers, and why BEGIN and END run once. Be able to write a count or a masking filter on the spot.
Know when a one-liner stops being acceptable: untested, hard to review, invisible in version control. Promote repeated one-liners into scripts and keep -i edits behind backups.
Treat one-liners as an operator's scratchpad, not a delivery mechanism: anything run by cron or CI belongs in a reviewed script with an exit status contract.
## Why one-liners still come up Operations work often needs a quick filter over a log: count the errors, mask a token, sum a column. Ruby's interpreter has flags borrowed from the classic text-processing tools that turn a short expression into a complete loop. Interviewers ask about them to see whether a candidate knows the tool beyond `ruby script.rb`. ## The flags | Flag | Effect | Globals involved | |---|---|---| | `-e CODE` | runs `CODE` as the program; may be given several times, each as a line | `$0` is `"-e"` | | `-n` | wraps the program in `while gets ... end` | `$_` holds the current line | | `-p` | like `-n`, then prints `$_` after each pass | `$_` is printed even if unchanged | | `-l` | with `-n`/`-p`: chops each line and sets `$\` to `$/` | `$\`, output record separator | | `-a` | with `-n`/`-p`: splits `$_` into `$F` | `$F`, split on `$;` | | `-F PATTERN` | sets the field separator used by `-a` | `$;` | | `-i[EXT]` | edits the named files in place, optionally keeping backups | | - **Where lines come from.** `gets` inside the loop reads the files named after the code, one after another, or standard input when none is named. - **`print` with no arguments** prints `$_`, which is why `print if cond` is the idiomatic filter under `-n`. - **Loop-only helpers.** When `-n` or `-p` is given, Ruby defines global `sub`, `gsub`, `chop` and `chomp` methods that operate on `$_` and store the result back into it. Without those flags they do not exist as bare calls. ## Three worked one-liners ```bash # 1. keep only error lines (grep-like) ruby -ne 'print if $_.include?("ERROR")' app.log # 2. mask secrets on every line (sed-like) ruby -pe 'gsub(/token=\S+/, "token=[FILTERED]")' app.log # 3. sum the second column (awk-like) ruby -lane 'BEGIN { total = 0 }; total += $F[1].to_i; END { puts total }' sizes.txt ``` 1. The first prints a line only when the condition holds, because `-n` never prints on its own. 2. The second prints **every** line, masked or not, because `-p` prints `$_` after each pass and `gsub` rewrote `$_` in place. 3. The third uses `-l` to drop the newline, `-a` to split fields into `$F`, and `BEGIN`/`END` so the accumulator is initialised once and reported once. ## `BEGIN` and `END` `BEGIN { ... }` runs before any other code of the file, and `END { ... }` registers a block that runs when the program finishes. Both must use braces, not `do ... end`. `END` registers its block only once, even though the line sits inside the `-n` loop, so the report appears a single time after the last line. `END` blocks share the same exit-handler list as `at_exit`. ## Limits and traps - `-e`, `-n`, `-p`, `-l`, `-a` and `-i` cannot be placed in `RUBYOPT`; Ruby raises `RuntimeError` (`invalid switch in RUBYOPT`) if they are. - Short switches combine (`-lane`), but the one that takes an argument (`-e`) must come last in the cluster. - Beyond a few lines, move the logic into a script: one-liners have no tests, and `$_`/`$F` read poorly in code review. - Under `-p`, forgetting that `sub`/`gsub` write back to `$_` leads people to add `print`, which doubles every line. ## Record separators and whole-file mode - `-0` followed by octal digits sets the input record separator `$/`: `-00` selects paragraph mode (records split on blank lines), and a value above `0377`, such as `-0777`, sets `$/` to `nil`, so each `gets` returns a whole file. - `-F` accepts a pattern, so `-F,` or `-F'\t'` splits comma- or tab-separated fields for `-a`. - Because `-l` also sets the output record separator, `print` under `-l` ends each record with a newline again after the chop. ```bash # whole-file mode: one line count per file named ruby -0777 -ne 'puts $_.count("\n")' app.log app.log.1 ``` ## When a script is the better answer A one-liner is fine at a prompt. Once it is saved in a crontab, a CI step or a README, rewrite it as a script with named variables, an explicit exit status and a test, because nobody reviews the flags packed into `-lane`.
- Why does `ruby -pe 'print gsub(/a/, "b")' file` print each line twice?`-p` already prints `$_` at the end of every pass. Under `-p`, the bare `gsub` rewrites `$_` and returns it, and the explicit `print` writes that same text again, so every line appears twice. Drop the `print`, or switch to `-n` if you only want some lines.
- Where do the lines read by `-n` come from when no file is named?The loop calls `gets`, which reads the files named after the code in order and falls back to standard input when none are named. So `cat app.log | ruby -ne '...'` and `ruby -ne '...' app.log` process the same lines.
saying these in an interview costs you the question
- -p prints only the lines that the code changed
- -n prints every line unless you call next
- BEGIN and END blocks run once per input line under -n
- Kernel#gsub with no receiver exists in every Ruby program
- -a works on its own without -n or -p