23

How can I interactively execute a command in Linux (zsh, if it matters) with a different umask from the default, for one command only? Perhaps a combination of commands combined in a single line? The new umask should apply only to that command and return to its default value for the next command entered.

Ideally the command would be agnostic to the default umask in force before its entered (in other words, the default umask doesn't have to specified).

3 Answers 3

32

Start a subshell:

(umask 22 && cmd)

Then the new umask will only alter that subshell.

Note that zsh executes the last command of the subshell in the subshell process instead of forking another one, which means that if that command is external, you're not even wasting a process, it's just that the fork is done earlier (so the umask is done in the child process that will later execute your command).

In shells such as bash, that don't do that optimisation already, you can use exec to explicitly request that no child process be spawned. Compare:

$ zsh -c '(umask 22 && ps -H); exit'
    PID TTY          TIME CMD
   3806 pts/0    00:00:00 zsh
   3868 pts/0    00:00:00   zsh
   3869 pts/0    00:00:00     ps
$ bash -c '(umask 22 && ps -H); exit'
    PID TTY          TIME CMD
   3806 pts/0    00:00:00 zsh
   3870 pts/0    00:00:00   bash
   3871 pts/0    00:00:00     bash
   3872 pts/0    00:00:00       ps
$ bash -c '(umask 22 && exec ps -H); exit'
    PID TTY          TIME CMD
   3806 pts/0    00:00:00 zsh
   3884 pts/0    00:00:00   bash
   3885 pts/0    00:00:00     ps

In bash, contrary to zsh, do not use exec for builtins or functions as that would not run the builtin / functions but try to run an external command by that name instead.

$ bash -c '(printf "%(%F)T\n" -1)'
2022-02-27
$ bash -c '(exec printf "%(%F)T\n" -1)'
printf: %(: invalid conversion specification

The latter called /usr/bin/printf which doesn't support %T (bash extension inspired by ksh93's). Use type cmd to check whether cmd is builtin or not in your particular version of bash.

3

You could wrap it in a shell script. umask inherits to all child processes.

cmd.sh:

umask 0022
cmd
3
  • Or, if you want to do it in one command line, you can invoke a shell like this, "$ (umask 0022; cmd)"
    – Rob Bos
    Feb 23, 2013 at 19:11
  • 2
    I confused $ ( with $( in your comment, now I understand: ( umask 0022; cmd ). Feb 23, 2013 at 20:21
  • whoops, well, at least someone clarified that ^^
    – Rob Bos
    Feb 23, 2013 at 23:05
1

Another trick is to save the file using install and process substitution. But that only works if the command will output its data to stdout and is only a single file.

install -m 600 <(cmd) newfile

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .