ChmodCalc

Symbolic vs Octal

rwxr-xr-x, 755 and u+x describe the same twelve bits. They are not interchangeable: one of them can express changes the other cannot. Type into either box below and watch both update.

chmod — file mode
whoreadwriteexecutedigit
Owner7
Group5
Others4
special bits — the fourth digit

ls -l → -rwxr-xr--

chmod 754 script.sh
chmod u=rwx,g=rx,o=r script.sh

Absolute symbolic form — identical result, and the only form that can name the setuid, setgid and sticky bits.

chmod ug+x script.sh

Relative form: what changed from 644. It leaves every other bit alone, which is what you want on a file you did not create.

what 754 grants on this file

  • Ownerrwxread the contents, change the contents and run it as a program
  • Groupr-xread the contents and run it as a program
  • Othersr--read the contents

programs that refuse this mode

  • OpenSSH client (ssh, scp, sftp, git over ssh)

    Permissions 0754 for 'script.sh' are too open.

    fix: chmod 600 script.sh

  • cron (cronie)

    (deploy) BAD FILE MODE (script.sh)

    fix: chmod 600 script.sh

The two notations, side by side

Octal packs three bits into one digit, so nine bits become three digits and each digit describes one class completely. Symbolic names the classes and permissions in letters: u for the owner, g for the group, o for others, a for all three, then an operator, then the permission letters.

The operator is the part that matters. = sets a class to exactly what follows, + adds, - removes. Only = has an octal equivalent, because octal has no way to say "leave the other bits alone". Take a file sitting at 644 that you now need to run: getting it to 755 is chmod 755 in octal, or chmod a+x relatively, or chmod u=rwx,g=rx,o=rx absolutely in symbolic form. All three land on the same mode; only the middle one is safe on a file whose current bits you have not read.

Where the fourth digit goes in an ls listing

ls -l prints nine characters, but a mode has twelve bits. The three special bits do not get columns of their own; they overload the three execute positions. This is the complete rule:

All twelve combinations of a special bit and the execute bit beneath it. The "character printed" column is taken from the rendered symbolic string for the mode in the same row, so it cannot disagree with the calculator above.
Position Special bit Execute bit Mode Renders as Character printed
owner execute clear clear 0000 --------- -
owner execute clear set 0100 --x------ x
owner execute setuid set clear 4000 --S------ S
owner execute setuid set set 4100 --s------ s
group execute clear clear 0000 --------- -
group execute clear set 0010 -----x--- x
group execute setgid set clear 2000 -----S--- S
group execute setgid set set 2010 -----s--- s
other execute clear clear 0000 --------- -
other execute clear set 0001 --------x x
other execute sticky set clear 1000 --------T T
other execute sticky set set 1001 --------t t

The pattern is uniform: lowercase means both bits are set, uppercase means the special bit is set and the execute bit beneath it is not. 2368 of the 4096 modes render with at least one uppercase letter.

What an uppercase letter does not tell you is that the bit is inert, and this is the part that is usually stated too strongly. It is exactly one class's execute bit that is missing, and only one of the three special bits is disabled by that:

  • Setgid on a regular file really is inert. The kernel tests both bits together — (mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) — so rw-r-Sr-- changes nothing about how the file runs.
  • Setuid on a file is not. The same function applies the owner's uid on mode & S_ISUID alone, without looking at S_IXUSR. Mode 4055 renders --Sr-xr-x and is a live setuid binary for anyone in the group, because some class can still execute it.
  • Setgid and sticky on a directory are not either. Group inheritance is decided by dir->i_mode & S_ISGID and deletion protection by S_ISVTX plus the directory's write bits; neither consults an execute bit. 2760 renders rwxrwS--- and still forces the group, and 1770 renders rwxrwx--T and is exactly the safe shared-group drop-box.

So an uppercase letter is worth a second look, not an automatic correction. On a regular file it usually is a mistake. On a directory it is often the mode you meant.

Sources: Linux kernel, fs/exec.c — bprm_fill_uid(); fs/inode.c — inode_init_owner(). C source read directly from the kernel’s master branch.

What each notation can and cannot say

6 intents, and how each notation expresses them. Only 1 of the 6 can be written as an octal command you could run without first reading the file's current mode, and 1 has no octal spelling at any mode.
Intent Symbolic Octal Note
make it executable by everyone chmod a+x file depends on the current mode Octal cannot express "add" — you must know all nine current bits to write the equivalent.
take write away from group and others chmod go-w file depends on the current mode This is the operation OpenSSH’s 022 mask is testing for.
set exactly rwxr-xr-x chmod u=rwx,g=rx,o=rx file chmod 755 file Absolute in both notations; identical result.
lock a class out entirely chmod go= file depends on the owner bits you want to keep An empty permission list after = clears the class. This is the shortest fix for a private key.
add the sticky bit and change nothing else chmod +t dir chmod 1xxx dir The octal form forces you to restate all nine lower bits, so it can silently undo something.
set the execute bit only where one already exists chmod -R a+X tree no octal equivalent Capital X is defined by POSIX and has no numeric spelling at all.

Memorise the capital-X row. POSIX defines it as "the execute/search portion of the file mode bits if the file is a directory or if the current (unmodified) file mode bits have at least one of the execute bits set". That conditional is what makes chmod -R a+X tree safe where chmod -R 755 tree is not: directories get their search bit, already-executable scripts keep theirs, and plain files are left alone. There is no way to write that in octal, because octal has no conditionals.

Source: POSIX.1-2024 (IEEE Std 1003.1-2024), XCU chmod — EXTENDED DESCRIPTION. The Open Group’s own free HTML edition, read directly.

Reading the leading character

An ls -l line starts with ten characters, not nine. The first is the file type and is not part of the mode at all: - for a regular file, d for a directory, l for a symbolic link, c and b for character and block devices, s for a socket, p for a FIFO. It cannot be changed with chmod, and the calculator above shows it as a separate toggle for exactly that reason.

A trailing character after the nine is not part of the mode either. A . or a + at the end of the field means an SELinux context or an access control list is attached — and the + in particular is a warning that the nine bits are no longer the whole story, because an ACL can grant access the mode does not show. If you are debugging a permission problem on a file whose listing ends in +, read getfacl before you read the mode.

Which to use

Use octal when you are stating a final result — provisioning a machine, writing a Dockerfile line, documenting an expected mode. It is idempotent and unambiguous, and a reviewer can check it against the intended value without knowing the previous state.

Use symbolic when you are making a change — fixing one bit on files you did not create, walking a tree that contains a mixture of executables and data, or touching a special bit without restating the nine below it. chmod +t /var/shared adds the sticky bit and changes nothing else; chmod 1777 /var/shared adds the sticky bit and also silently sets nine other bits to values you may not have intended.

## Questions people actually ask

What is the difference between symbolic and octal chmod?

Octal is absolute — chmod 644 f writes all nine bits and discards whatever was there. Symbolic can be relative — chmod u+x f changes one bit and leaves the other eight alone. On a file whose current mode you have not checked, the relative form is the safe one; on a file you are provisioning from scratch, the absolute form is clearer because it states the whole result.

What does rwxr-xr-x mean?

Three groups of three. rwx for the owner, r-x for the group, r-x for everyone else — which is 755 in octal. A dash means the bit is off. The characters are always in the order read, write, execute, so position tells you which permission you are looking at even when the letter is a dash.

Why does ls show an “s” instead of an “x”?

Because a special bit is set and it shares a column with the execute bit. s in the owner-execute position means setuid and execute; s in the group position means setgid and execute; t in the other position means the sticky bit and execute. There is no separate column for the fourth octal digit, so ls overloads three characters instead.

What does a capital S or T mean in ls output?

That the special bit is set and the execute bit in that same class is not. 2368 of the 4096 modes render with at least one uppercase letter. Whether that is a mistake depends on the target: on a regular file it usually is — setgid without group-execute does nothing at all — but on a directory the setgid and sticky bits are tested on their own, so rwxrwx--T is a working shared drop-box rather than a typo. Check what you meant to set; do not assume the bit is dead.

Can I write everything in octal?

Not everything. Three-digit octal reaches 512 of the 4096 modes; the remaining 3584 need a fourth digit or a symbolic operand. And no octal argument can express a relative change or the capital-X conditional — those exist only in the symbolic form.

Which form should I use in a deployment script?

Octal for provisioning, because it is idempotent and states the entire intended result: run it twice and the mode is the same. Symbolic for repair, because it changes only what you name. A script that sets up a fresh machine should use octal; a script that fixes one bit on files somebody else owns should use symbolic.

elsewhere on this site

other tools we run