Skip to content
shellmap

Trim leading and trailing whitespace

Remove only the whitespace at the start and end of each line (preserving internal spaces) — for cleaning user input, config-file values, and form fields.

How to trim leading and trailing whitespace in each shell

Bashunix
sed "s/^[[:space:]]*//;s/[[:space:]]*$//" input.txt

`^` anchors to start; `$` to end; `[[:space:]]*` matches zero-or-more whitespace (POSIX character class, portable across GNU + BSD). Two substitutions in one `sed` invocation. Preserves internal spaces.

Zshunix
sed "s/^[[:space:]]*//;s/[[:space:]]*$//" input.txt
Fishunix
sed "s/^[[:space:]]*//;s/[[:space:]]*$//" input.txt
PowerShellwindows
(Get-Content input.txt) | ForEach-Object { $_.Trim() }

`.Trim()` is `[String]`-method — strips Unicode whitespace including tabs and CRLF. Variants: `.TrimStart()`, `.TrimEnd()`, `.Trim("abc")` (trim specific chars). Faster than regex anchors for large inputs.

cmd.exewindows
powershell -NoProfile -Command "(Get-Content input.txt) | ForEach-Object { $_.Trim() }"

cmd has no per-line trim. The `set "var=%var: =%"` trick removes ALL spaces (including internal — wrong for "trim ends only"). Always shell out.

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • **bash parameter expansion `${var##*[[:space:]]}` does NOT trim** — it removes the LONGEST prefix matching `*[[:space:]]` (greedy). For string `" hello world "` the longest match for `*[[:space:]]` is `" hello world "` itself (the trailing space is at the very end and `*` matches up to it) — result is empty. The correct parameter-expansion trim is bash 4+: `${var# }` (strip one leading space) chained, or use `read` with whitespace as IFS (`read -r var <<< "$input"` strips leading/trailing because of how `read` parses fields). The safest portable answer is `sed "s/^[[:space:]]*//;s/[[:space:]]*$//"` — `sed` is on every Unix system and the semantics are unambiguous.
  • **`awk \'{$1=$1; print}\'` (the whitespace-normalize idiom) ALSO trims ends — but it COLLAPSES internal whitespace as a side effect** (any run of internal whitespace becomes one space, the OFS). If you ONLY want to trim ends and PRESERVE internal spacing, sed is the right tool, not awk-with-`$1=$1`. For "trim AND collapse" both at once, awk wins. For "trim only", sed wins. Be deliberate about which you need — many bugs come from accidentally collapsing internal whitespace when you only meant to trim.
  • **pwsh `.Trim()` is Unicode-aware** — strips not just ASCII space but also tab, CRLF, NBSP (U+00A0), and most Unicode "whitespace" codepoints (it calls `Char.IsWhiteSpace`). Subtle: `Char.IsWhiteSpace` does NOT include zero-width characters (U+200B etc.) — those require explicit `-replace "[\u200B-\u200D\uFEFF]", ""` removal. For pwsh CSV columns whose value occasionally has leading/trailing spaces (from sloppy CSV producers): `Import-Csv file.csv | ForEach-Object { $_ | Add-Member -NotePropertyName name -NotePropertyValue $_.name.Trim() -Force }` — but easier: `(Get-Content file.csv -Raw).Trim()` line-by-line then re-`ConvertFrom-Csv`.
  • **Trim-during-read idioms**: bash `read` automatically strips leading + trailing IFS (default `<space><tab><newline>`): `while IFS= read -r line; do …; done` PRESERVES whitespace (because we cleared IFS); `while read line; do …; done` TRIMS leading/trailing automatically. So if your script reads a config file with `while read key value; do …; done`, the values are auto-trimmed — which is usually what you want, but surprising when you wanted exact bytes. fish: `read line` similarly strips. pwsh `Get-Content` does NOT trim (lines come back including embedded whitespace except the final newline). Awareness of this asymmetry prevents "why does this work in bash but not pwsh" bugs.

Related commands

Related tasks