How to check if s permission bit is set on Linux shell? or Perl?
If you're using perl, then have a look at perldoc:
-u File has setuid bit set.
-g File has setgid bit set.
-k File has sticky bit set.
So something like:
if (-u $filename) { ... }
Get permission bits string on Linux/Perl?
Why not just convert your decimial number to binary form and use binary AND or XOR?
This code gives you binary permissions:
printf '%b', (stat 'filename.txt')[2] & 07777;
For example, -rw-r----- binary form will be 110100000.
How to check availability of Perl, its version and presence of a required module?
if perl -MJSON::Any -e 'print "$JSON::Any::VERSION\n"' >/dev/null 2>&1
then : OK
else echo "Cannot find a perl with JSON::Any installed" 1>&2
exit 1
fi
I often use '${PERL:-perl}
' and similar constructs to identify the command (for awk
vs nawk
or gawk
; troff
vs groff
; etc).
If you want to test the version of JSON::Any, capture the output from the command instead.
If you want to test the version of Perl, add 'use 5.008009;
' or whatever number you think is sensible. (It wasn't so long ago that they finally removed Perl 4 from one of the NFS-mounted file systems at work - but that was not the only Perl on the machine - at least, not in the last decade or more!)
Determine if a file is script or not
You can certainly trust file to find any script in the directory you specify:
file /usr/bin/* | grep script
Or, if you prefer to do it yourself and you are using bash you can do:
for f in /usr/bin/*; do r=$(head -1 $f | grep '^#! */') && echo "$f: $r"; done
which uses the shebang to determine the interpreter and thus the script entity.
How can I get octal file permissions with Ruby?
If you check section two of your libc manual (man 2 stat
from the shell) you should see something like this:
The status information word st_mode has the following bits:
#define S_IFMT 0170000 /* type of file */
#define S_IFIFO 0010000 /* named pipe (fifo) */
#define S_IFCHR 0020000 /* character special */
#define S_IFDIR 0040000 /* directory */
#define S_IFBLK 0060000 /* block special */
#define S_IFREG 0100000 /* regular */
#define S_IFLNK 0120000 /* symbolic link */
#define S_IFSOCK 0140000 /* socket */
#define S_IFWHT 0160000 /* whiteout */
#define S_ISUID 0004000 /* set user id on execution */
#define S_ISGID 0002000 /* set group id on execution */
#define S_ISVTX 0001000 /* save swapped text even after use */
#define S_IRUSR 0000400 /* read permission, owner */
#define S_IWUSR 0000200 /* write permission, owner */
#define S_IXUSR 0000100 /* execute/search permission, owner */
The precise contents won't be exactly the same but the octal values should be the same on any Unixy system.
The part you're interested in would be the "this is a regular file" bit:
#define S_IFREG 0100000 /* regular */
That's where your leading 100
comes from.
If you look back at the Perl version, you'll see that they're applying a bit mask:
(stat)[2] & 07777
^^^^^^^
to grab just the permission bits. If you do the same in Ruby:
printf "%04o\t#{f}\n", (File.stat(f).mode & 07777)
# ----------------------------------------^^^^^^^
you'll get the sort of output that you're expecting.
If you don't have libc man pages then you can look at the OpenGroup's stat
documentation which will point you at the struct stat
documentation which covers the various bits in the mode:
┌─────────┬───────────┬───────────────────────────────────────────┐
│ Name │ Numeric │ Description │
│ │ Value │ │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IRWXU │ 0700 │ Read, write, execute/search by owner. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IRUSR │ 0400 │ Read permission, owner. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IWUSR │ 0200 │ Write permission, owner. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IXUSR │ 0100 │ Execute/search permission, owner. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IRWXG │ 070 │ Read, write, execute/search by group. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IRGRP │ 040 │ Read permission, group. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IWGRP │ 020 │ Write permission, group. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IXGRP │ 010 │ Execute/search permission, group. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IRWXO │ 07 │ Read, write, execute/search by others. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IROTH │ 04 │ Read permission, others. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IWOTH │ 02 │ Write permission, others. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_IXOTH │ 01 │ Execute/search permission, others. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_ISUID │ 04000 │ Set-user-ID on execution. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_ISGID │ 02000 │ Set-group-ID on execution. │
├─────────┼───────────┼───────────────────────────────────────────┤
│ S_ISVTX │ 01000 │ On directories, restricted deletion flag. │
└─────────┴───────────┴───────────────────────────────────────────┘
understanding and decoding the file mode value from stat function output
The mode from your question corresponds to a regular file with 644 permissions (read-write for the owner and read-only for everyone else), but don’t take my word for it.
$ touch foo
$ chmod 644 foo
$ perl -le 'print +(stat "foo")[2]'
33188
The value of $mode
can be viewed as a decimal integer, but doing so is not particularly enlightening. Seeing the octal representation gives something a bit more familiar.
$ perl -e 'printf "%o\n", (stat "foo")[2]'
100644
Bitwise AND with 07777 gives the last twelve bits of a number’s binary representation. With a Unix mode, this operation gives the permission or mode bits and discards any type information.
$ perl -e 'printf "%d\n", (stat "foo")[2] & 07777' # decimal, not useful
420
$ perl -e 'printf "%o\n", (stat "foo")[2] & 07777' # octal, eureka!
644
A nicer way to do this is below. Read on for all the details.
Mode Bits
The third element returned from stat
(which corresponds to st_mode
in struct stat
) is a bit field where the different bit positions are binary flags.
For example, one bit in st_mode
POSIX names S_IWUSR
. A file or directory whose mode has this bit set is writable by its owner. A related bit is S_IROTH
that when set means other users (i.e., neither the owner nor in the group) may read that particular file or directory.
The perlfunc documentation for stat
gives the names of commonly available mode bits. We can examine their values.
#! /usr/bin/env perl
use strict;
use warnings;
use Fcntl ':mode';
my $perldoc_f_stat = q(
# Permissions: read, write, execute, for user, group, others.
S_IRWXU S_IRUSR S_IWUSR S_IXUSR
S_IRWXG S_IRGRP S_IWGRP S_IXGRP
S_IRWXO S_IROTH S_IWOTH S_IXOTH
# Setuid/Setgid/Stickiness/SaveText.
# Note that the exact meaning of these is system dependent.
S_ISUID S_ISGID S_ISVTX S_ISTXT
# File types. Not necessarily all are available on your system.
S_IFREG S_IFDIR S_IFLNK S_IFBLK S_IFCHR S_IFIFO S_IFSOCK S_IFWHT S_ENFMT
);
my %mask;
foreach my $sym ($perldoc_f_stat =~ /\b(S_I\w+)\b/g) {
my $val = eval { no strict 'refs'; &$sym() };
if (defined $val) {
$mask{$sym} = $val;
}
else {
printf "%-10s - undefined\n", $sym;
}
}
my @descending = sort { $mask{$b} <=> $mask{$a} } keys %mask;
printf "%-10s - %9o\n", $_, $mask{$_} for @descending;
On Red Hat Enterprise Linux and other operating systems in the System V family, the output of the above program will be
S_ISTXT - undefined
S_IFWHT - undefined
S_IFSOCK - 140000
S_IFLNK - 120000
S_IFREG - 100000
S_IFBLK - 60000
S_IFDIR - 40000
S_IFCHR - 20000
S_IFIFO - 10000
S_ISUID - 4000
S_ISGID - 2000
S_ISVTX - 1000
S_IRWXU - 700
S_IRUSR - 400
S_IWUSR - 200
S_IXUSR - 100
S_IRWXG - 70
S_IRGRP - 40
S_IWGRP - 20
S_IXGRP - 10
S_IRWXO - 7
S_IROTH - 4
S_IWOTH - 2
S_IXOTH - 1
Bit twiddling
The numbers above are octal (base 8), so any given digit must be 0-7 and has place value 8n, where n is the zero-based number of places to the left of the radix point. To see how they map to bits, octal has the convenient property that each digit corresponds to three bits. Four, two, and 1 are all exact powers of two, so in binary, they are 100, 10, and 1 respectively. Seven (= 4 + 2 + 1) in binary is 111, so then 708 is 1110002. The latter example shows how converting back and forth is straightforward.
With a bit field, you don’t care exactly what the value of a bit in that position is but whether it is zero or non-zero, so
if ($mode & $mask) {
tests whether any bit in $mode
corresponding to $mask
is set. For a simple example, given the 4-bit integer 1011 and a mask 0100, their bitwise AND is
1011
& 0100
------
0000
So the bit in that position is clear—as opposed to a mask of, say, 0010 or 1100.
Clearing the most significant bit of 1011 looks like
1011 1011
& ~(1000) = & 0111
------
0011
Recall that ~
in Perl is bitwise complement.
For completeness, set a bit with bitwise OR as in
$bits |= $mask;
Octal and file permissions
An octal digit’s direct mapping to three bits is convenient for Unix permissions because they come in groups of three. For example, the permissions for the program that produced the output above are
-rwxr-xr-x 1 gbacon users 1096 Feb 24 20:34 modebits
That is, the owner may read, write, and execute; but everyone else may read and execute. In octal, this is 755—a compact shorthand. In terms of the table above, the set bits in the mode are
S_IRUSR
S_IWUSR
S_IXUSR
S_IRGRP
S_IXGRP
S_IROTH
S_IXOTH
We can decompose the mode from your question by adding a few lines to the program above.
my $mode = 33188;
print "\nBits set in mode $mode:\n";
foreach my $sym (@descending) {
if (($mode & $mask{$sym}) == $mask{$sym}) {
print " - $sym\n";
$mode &= ~$mask{$sym};
}
}
printf "extra bits: %o\n", $mode if $mode;
The mode test has to be more careful because some of the masks are shorthand for multiple bits. Testing that we get the exact mask back avoids false positives when some of the bits are set but not all.
The loop also clears the bits from all detected hits so at the end we can check that we have accounted for each bit. The output is
Bits set in mode 33188:
- S_IFREG
- S_IRUSR
- S_IWUSR
- S_IRGRP
- S_IROTH
No extra warning, so we got everything.
That magic 07777
Converting 77778 to binary gives 0b111_111_111_111
. Recall that 78 is 1112, and four 7s correspond to 4×3 ones. This mask is useful for selecting the set bits in the last twelve. Looking back at the bit masks we generated earlier
S_ISUID - 4000
S_ISGID - 2000
S_ISVTX - 1000
S_IRWXU - 700
S_IRWXG - 70
S_IRWXO - 7
we see that the last 9 bits are the permissions for user, group, and other. The three bits preceding those are the setuid, setgroupid, and what is sometimes called the sticky bit. For example, the full mode of sendmail
on my system is -rwxr-sr-x
or 3428510. The bitwise AND works out to be
(dec) (oct) (bin)
34285 102755 1000010111101101
& 4095 = & 7777 = & 111111111111
------- -------- ------------------
1517 = 2755 = 10111101101
The high bit in the mode that gets discarded is S_IFREG
, the indicator that it is a regular file. Notice how much clearer the mode expressed in octal is when compared with the same information in decimal or binary.
The stat
documentation mentions a helpful function.
… and the
S_IF*
functions are
S_IMODE($mode)
the part of$mode
containing the permission bits and the setuid/setgid/sticky bits
In ext/Fcntl/Fcntl.xs
, we find its implementation and a familiar constant on the last line.
void
S_IMODE(...)
PREINIT:
dXSTARG;
SV *mode;
PPCODE:
if (items > 0)
mode = ST(0);
else {
mode = &PL_sv_undef;
EXTEND(SP, 1);
}
PUSHu(SvUV(mode) & 07777);
To avoid the bad practice of magic numbers in source code, write
my $permissions = S_IMODE $mode;
Using S_IMODE
and other functions available from the Fcntl module also hides the low-level bit twiddling and focuses on the domain-level information the program wants. The documentation continues
S_IFMT($mode)
the part of$mode
containing the file type which can be bit-anded with (for example)S_IFREG
or with the following functions# The operators -f, -d, -l, -b, -c, -p, and -S.
S_ISREG($mode) S_ISDIR($mode) S_ISLNK($mode)
S_ISBLK($mode) S_ISCHR($mode) S_ISFIFO($mode) S_ISSOCK($mode)
# No direct -X operator counterpart, but for the first one
# the -g operator is often equivalent. The ENFMT stands for
# record flocking enforcement, a platform-dependent feature.
S_ISENFMT($mode) S_ISWHT($mode)
Using these constants and functions will make your programs clearer by more directly expressing your intent.
Looking for a more efficient (and portable) way to obtain (numeric) file permissions in unix
I don't believe this is any more elegant/efficient than your own version but I will put it up in case any of the techniques are useful for improving your own. This could obviously be done much more simply/elegantly with a script though
The basic premise is to use tr to translate the rwx to the relevant octal numbers, then use sed to split into groups of 3 add pluses and then generate an awk command string whicg gets passed to awk to add them up.
ls -ld / | \
cut -c2-10 | \
tr 'rwx-t' '42100' | \
sed -E -e 's/(...)(...)(...)/\1 \2 \3/g' \
-e 's/([0-9])([0-9])([0-9])/\1+\2+\3/g' \
-e 's/^(.*)$/BEGIN {print \1}/g'|\
awk -f -`
Related Topics
Can Someone Explain the Shell Shock Bash Code
Unix How to Block Unix/Linux 'Wall' Messaging
Pthread Condition Variables on Linux, Odd Behaviour
Significance of Address 0X8048080
Random Alphanumeric String Linux Swift 3
Sed: Find Pattern Over Two Lines, Not Replace After That Pattern
Ada Compiler Crashes with "Ada Compiler Not Installed on This System." After Downgrading Gcc Version
Bash - Concatenating Variable on to Path
How to Run Ionic in the Background
Raspberry Pi Refusing Connection to Bottle Server
Undefined Reference to 'Pthread_Init' When Using -Lpthread Flag:
Serialport in Mono in Linux Not Responding to Datareceived Event
How to Identify the User Who Owns a Process from /Proc/Pid
"Echo" Output Different Answer by Sh and Bash
Hash ("#") Symbol in /Etc/Environment Causes String to Be Split
Bash -C Variable Does Not Get Assigned
Bash Script to Remove Directories Based on Modified File Date