How to set specific file permissions when redirecting output?

12,762

Solution 1

There's no way to do it while piping as far as I know, a simple script might be the best solution.

if [ -e /tmp/foo.log ]; then
    foo >> /tmp/foo.log
else
    foo >> /tmp/foo.log
    chmod 0644 /tmp/foo.log
fi

Solution 2

I know it's an old question, but I wanted to add my two cents.

I had the same idea and came up with a solution similar to BowlesCR. The problem with his solution was that my command (foo) wouldn't work if I changed the umask before running it, so this is my take on the problem:

foo | ( umask 0033; cat >> /tmp/foo.log; )

Here, umask only affects the redirection to foo.log in the subshell. Everything else remains unaffected.

A bit convoluted, but it works.

Solution 3

Without true scripting, you can chain a bit:

touch /tmp/foo.log; chmod 0644 /tmp/foo.log; foo >> /tmp/foo.log

Effectively similar to Slowki's answer, but condensed into a one-liner.

The only other thing I can think of is tinkering with the umask. Best to do this in a subshell so it doesn't pollute the current environment:

(umask 0033 && foo >> /tmp/foo.log)

Two issues with that, though.

  1. Umask can't raise the permissions above the level specified in the creat() syscall (0666 appears to be what Bash uses).
  2. This won't change the permissions on an existing file (because umask applies only to file creation).

Solution 4

When the redirection sets the wrong permissions, e.g.:

$ rm capture.*
$ perl -e 'print STDERR "$$ STDERR\n"; print STDOUT "$$ STDOUT\n"' \
                            >> capture.STDOUT 2>> capture.STDERR
$ perl -e 'print STDERR "$$ STDERR\n"; print STDOUT "$$ STDOUT\n"' \
                            >> capture.STDOUT 2>> capture.STDERR
$ ls -l capture.*
-rw-rw-rw- 1 jcookinf jcookinf 22 Jun 12 10:38 capture.STDERR
-rw-rw-rw- 1 jcookinf jcookinf 22 Jun 12 10:38 capture.STDOUT
$ cat capture.*
215 STDERR
216 STDERR
215 STDOUT
216 STDOUT

I believe you can use something like xynomorf gave, and address the stderr concern from Orsiris de Jong with a slight modification to use the bash process substitution.

$ rm capture.*
$ perl -e 'print STDERR "$$ STDERR text\n"; print STDOUT "$$ STDOUT text\n"' \
            > >(umask 0033; cat >> capture.STDOUT) 2> >(umask 0033; cat >> capture.STDERR)
$ perl -e 'print STDERR "$$ STDERR text\n"; print STDOUT "$$ STDOUT text\n"' \
            > >(umask 0033; cat >> capture.STDOUT) 2> >(umask 0033; cat >> capture.STDERR)
$ ls -l capture.*
-rw-r--r-- 1 jcookinf jcookinf 32 Jun 12 10:43 capture.STDERR
-rw-r--r-- 1 jcookinf jcookinf 32 Jun 12 10:43 capture.STDOUT
$ cat capture.*
233 STDERR text
238 STDERR text
233 STDOUT text
238 STDOUT text

Naturally, use umask 0077 to get mode 600 and prohibit any other users from seeing the contents.

Share:
12,762
Patrick M
Author by

Patrick M

Negative space.

Updated on September 18, 2022

Comments

  • Patrick M
    Patrick M over 1 year

    This is probably a duplicate, but all my searches are turning up questions about permission denied errors.

    I am running a command in a bash shell. I want to redirect output to append to a file that probably does not exist on the first run. I want to set specific file permissions mode if output redirection has to create this file. Is there a way to do this with one command?

    For example, I might try

    foo >> /tmp/foo.log 0644
    

    where 0644 are the permissions I want foo.log to end up with. Most commands I've experimented with in bash end up interpreting 0644 as an additional argument to foo.

    I get the feeling that this is going to take a second command to chmod the permissions before or after writing to it.

    I am using GNU bash 4.2.25 and Ubuntu 12.04, if that makes a difference - general answers are preferred.

  • Patrick M
    Patrick M over 10 years
    Thanks Slowki, that was my hunch as well. I'm going to leave it open for a few days, in the hopes of attracting a guru to enlighten us.
  • Patrick M
    Patrick M over 10 years
    I'm still new enough to *nix that the umask is still a bit of magic to me. Thanks for the tip, I'll be sure to read up on it.
  • proski
    proski over 8 years
    The problem with this approach is that it creates a short time window when the permissions on the file are wrong. I would not use it if the goal is to protect sensitive data.
  • Orsiris de Jong
    Orsiris de Jong about 7 years
    Very nice and effective :) Just can't be used with stderr redirection on top of stdout redir.
  • Feuermurmel
    Feuermurmel about 7 years
    Hmm … Still allows an unprivileged process from opening the file and later read its content.
  • Scott - Слава Україні
    Scott - Слава Україні almost 5 years
    This answer works, as far as it goes, but @proski is right:  the umask answers are better, and one of them should be accepted.
  • Scott - Слава Україні
    Scott - Слава Україні almost 5 years
    (1) Congratulations on finding an interesting, useful use of cat.  (Although this doesn’t follow the standard pattern of useless uses of cat.)  (2) I presume that you meant to say that bash defaults to mode 0666 when creating files, and so I edited your answer to say so.  (See this, this  … (Cont’d)
  • Scott - Слава Україні
    Scott - Слава Україні almost 5 years
    (Cont’d) …  and this.)  And the question specifically mentions mode 0644.  So a umask value of 22, 23 or 32 would work as well, in this context (you don’t need to use leading zero(s); when modes and umask values are specified numerically, they are always interpreted as octal).  22 is more commonly, conventionally used.
  • Scott - Слава Україні
    Scott - Слава Україні almost 5 years
    @Feuermurmel: So what? The question explicitly calls for mode 644.  We have to assume that the OP knows that 644 means world-readable and intended to make his information public.
  • Feuermurmel
    Feuermurmel almost 5 years
    @Scott I wrote that comment a long time ago. I have no idea why I mentioned that. Maybe I was thinking of write access instead …