opscape

Text processing

iconv

Convert text from one character encoding to another, such as Latin-1 or UTF-16 to UTF-8.

iconv [-f FROM] [-t TO] [OPTION...] [FILE...]

10 of 10 examples run in Debian 13.7 on 2026-10-31 · manual: iconv(1)

iconv re-encodes text: it reads bytes in the encoding given by -f (from) and writes the same characters in the encoding given by -t (to). Without -f or -t it uses the current locale's encoding, which here is UTF-8. On Linux it is part of the C library (glibc), so it is always present.

You need it when text arrives from somewhere that does not use UTF-8: CSV exports from older Windows software (often CP1252), legacy files in ISO-8859-1 (Latin-1), or UTF-16 files with a byte-order mark. The symptom is usually é where é should be, or grep and sed complaining about binary data.

iconv cannot detect the input encoding; you have to know it. file gives a reasonable guess. When a character has no equivalent in the target encoding, conversion stops with an error unless you ask for //TRANSLIT (approximate it) or -c (drop it).

Examples

Convert a Latin-1 file to UTF-8

Setup run before this example
printf 'caf\351 cr\350me br\373l\351e\n' > legacy.txt
file legacy.txt
iconv -f ISO-8859-1 -t UTF-8 legacy.txt
output
legacy.txt: ISO-8859 text
café crème brûlée

file identifies the bytes as ISO-8859 text. iconv reads each accented letter as one Latin-1 byte and writes it as the two-byte UTF-8 sequence the terminal expects.

The same character in two encodings

printf 'é\n' | od -An -t x1
printf 'é\n' | iconv -f UTF-8 -t ISO-8859-1 | od -An -t x1
output
 c3 a9 0a
 e9 0a

In UTF-8, é is the two bytes c3 a9; in Latin-1 it is the single byte e9. Reading one as the other is what produces garbled text.

Write the result to a new file

Setup run before this example
printf 'caf\351 cr\350me br\373l\351e\n' > legacy.txt
iconv -f ISO-8859-1 -t UTF-8 -o fixed.txt legacy.txt
file fixed.txt
output
fixed.txt: Unicode text, UTF-8 text

-o names the output file. Never redirect onto the input file: the shell would empty it before iconv reads it.

Windows-1252 has characters Latin-1 lacks

printf 'Price: 5\200\n' | iconv -f CP1252 -t UTF-8
explain
output
Price: 5€

Byte 0x80 is the euro sign in Windows-1252 but a control character in ISO-8859-1. CSV files saved by Windows software are usually CP1252, so choose that rather than Latin-1 when you see € or curly quotes.

A character the target cannot represent

printf 'café\n' | iconv -f UTF-8 -t ASCII
explain
outputexit 1
iconv: illegal input sequence at position 3
caf

ASCII has no é. iconv writes what it converted so far (caf), reports the byte position of the problem and exits with 1.

Approximate missing characters

echo 'Grüße – “naïve” café' | iconv -f UTF-8 -t ASCII//TRANSLIT
explain
output
Grusse - "naive" cafe

//TRANSLIT replaces each character with the closest ASCII text: ü becomes u, ß becomes ss, the en dash a hyphen and curly quotes straight ones. Handy for slugs and file names.

Check that a file is valid UTF-8

Setup run before this example
printf 'caf\351\n' > legacy.txt
iconv -f UTF-8 -t UTF-8 todo.txt > /dev/null && echo "todo.txt: valid UTF-8"
iconv -f UTF-8 -t UTF-8 legacy.txt > /dev/null || echo "legacy.txt: not UTF-8"
output
todo.txt: valid UTF-8
iconv: illegal input sequence at position 3
legacy.txt: not UTF-8

Converting UTF-8 to UTF-8 changes nothing but fails on invalid byte sequences, which makes it a quick validity check: the exit status tells you the answer.

Drop invalid bytes

Setup run before this example
printf 'caf\351\n' > legacy.txt
iconv -c -f UTF-8 -t UTF-8 legacy.txt
echo "exit status: $?"
output
caf
exit status: 1

-c silently omits characters that cannot be converted, here the stray Latin-1 byte. Note that glibc's iconv still exits with status 1 when it had to drop something.

UTF-16 and the byte-order mark

printf 'hi\n' | iconv -f UTF-8 -t UTF-16 | od -An -t x1
printf 'hi\n' | iconv -f UTF-8 -t UTF-16LE | od -An -t x1
output
 ff fe 68 00 69 00 0a 00
 68 00 69 00 0a 00

Plain UTF-16 output starts with the byte-order mark ff fe (little-endian here) followed by two bytes per character. Naming the byte order explicitly, UTF-16LE, leaves the mark out.

List the supported encodings

iconv -l | grep -i -m 3 '^utf-16'
explain
output
UTF-16//
UTF-16BE//
UTF-16LE//

iconv -l prints every encoding name and alias glibc knows, over a thousand of them; grep narrows the list.

Common pitfalls

  • iconv -f X -t Y file > file destroys the file. Use -o newfile and rename, or a temporary file.

  • Guessing the source encoding wrong does not always fail: any byte sequence is valid Latin-1, so converting a UTF-8 file "from Latin-1" silently produces é-style garbage. Check with file and a quick look at the result.

  • Converting an already-UTF-8 file again (double encoding) is the most common way to create mojibake. Validate with iconv -f UTF-8 -t UTF-8 first.

  • Files from Windows may also have CRLF line endings and a UTF-8 BOM; iconv does not touch either. Use tr -d '\r' or sed '1s/^\xEF\xBB\xBF//' for those.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • macOS ships a different iconv (from libiconv) with the same -f, -t, -c, -l and //TRANSLIT, but transliteration results and encoding names can differ from glibc's. The -o option is a glibc feature; use redirection to a new file on macOS.

Options worth knowing

A selection, described in our own words. For the full list, see iconv(1) or iconv --help.

OptionWhat it does
-f, --from-code ENCODINGEncoding of the input.
-t, --to-code ENCODINGEncoding of the output; append //TRANSLIT or //IGNORE to handle unconvertible characters.
-cOmit characters that cannot be converted.
-o, --output FILEWrite the output to FILE.
-l, --listList all known encodings.
-s, --silentSuppress warnings.

Related