74

I am using the following 2 commands to create a 0B file and set its extn to 644

touch filename.ext
chmod 777 filename.txt

My question is that whether there is any single command in unix (korn shell) that will do the two things together that is to say create a 0B fil with desired permission?

Caleb
  • 5,084
  • 1
  • 46
  • 65
user3075776
  • 741
  • 1
  • 5
  • 4
  • 5
    Whatever it is that you are hoping to achieve **`chmod 777` is *wrong* and *dangerous!*** You should adjust your code to suitable permissions (in most scenarios that's something like 755); and if you have had world writable system files on a public-facing host, reimage the system with sane permissions, and start looking into whether you have been breaking laws through criminal negligence. – tripleee Feb 22 '19 at 19:26
  • “set its extn to 644”←I don’t know what ‘extn’ stands for, but seems like mode 644 was the desired outcome. Too late to edit now, since all the answers already assume 777. – Amir Jun 01 '22 at 06:53

8 Answers8

81
install -m 777 /dev/null filename.txt
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
proski
  • 3,603
  • 27
  • 27
17

For bash, simply use chmod with file redirection and history expansion:

chmod 777 filename.txt>>!#:2

For ksh and zsh you have to drop the history expansion (as shown above, there may be other ways) and use:

chmod 644 filename>>filename

For scripts of any shell you don't have (and really don't need) history expansion so use the above there as well.

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • 2
    Using this trick a file note still gets created with default umask permissions before getting changed. It is possible for this to fail (power outage, weird ACLs, file system issues, etc.) and leave a file on the system with the wrong permissions. This is a one line shell command but is executed by the shell in two steps. [Setting the umask](https://stackoverflow.com/a/21343912/313192) is the proper way to ensure a file is created with the desired permissions at the time it is created. – Caleb Jul 05 '18 at 09:07
  • This may be the best approach but less readable. May you explain about the `!#:2` part? – midnite Apr 15 '22 at 19:49
  • 1
    @midnite `!#:2` is a history command: `!` marks the start of a history command; `#` refers to *this* command; `:2` refers to the second argument (of this command which is `filename.txt`). – Paul Evans Apr 17 '22 at 11:03
  • I assume the `>>` redirections here should be `>`, since appending in this context wouldn't make sense. Also, it doesn't make sense to redirect the output of `chmod` to the file. – Ken Williams May 03 '22 at 15:43
  • @PaulEvans I know it "works" with `>>`, but that's only when the file doesn't already exist. If it already exists with content, you're going to get the wrong content in it (OP wants "a 0B file"). `>` is the correct version. Also, redirecting the output of `chmod` really makes no sense. It makes it look like there's output to redirect, which there's not. This approach also depends on subtle behavior of the shell (creating the output redirection file first, THEN running `chmod`) and still suffers from the same problem (creating file and then changing permissions in two non-atomic steps). – Ken Williams May 04 '22 at 20:36
  • @KenWilliams The OP is: *to create a 0B file*. – Paul Evans May 04 '22 at 21:37
  • It is worth noting that using chmod will set the permissions exactly as given. If you still want (default) ACLs to being effective here (e.g. to restrict permissions for "other" this is the wrong command. – uli42 Sep 02 '22 at 15:01
17

First of all, you should NEVER be setting anything to 777 permissions. This is a huge security problem and there just isn't any need for it. For the purposes of this question I will assume you want to create a file with more secure permissions than the defaults, say 600.

There is not a one stop way to safely create AND change the permissions of a file using most bash tools. Even the tricky redirection trick in Paul's answer actually momentarily creates a file with default permissions before resetting them. All new files get created with whatever the system umask value is set too unless the creating program sends very specific requests to the operating system at the time of node creation. A mask of 133 is common, meaning your files get created with 644 permissions out of the box.

Besides using the chmod command to set the file permissions after you create a file, you can also tell the system what defaults you want using the umask command.

$ umask 077
$ touch test_file
$ ls -l test_file
-rw------- 1 user group 0 Jan 24 22:43 test_file

You will note the file has been created with 600 permissions.

This will stay in effect for all commands run in a shell until you either close that shell or manually set another value. If you would like to use this construct to run a single command (or even a small set of them) it can be useful to isolate the commands in a subshell

$ (umask 077 ; touch test_file)

Note that anything else you put inside the parens will use that umask but as soon as you close it, you are back in your previous environment.

Caleb
  • 5,084
  • 1
  • 46
  • 65
  • 1
    `NEVER be setting anything to 777` what it means? `upload` folders on all my websites under nginx feel themself perfect – Vasilii Suricov Feb 28 '20 at 16:38
  • 1
    @Vasilii Then your web server is as full of holes as a cheese cloth. It is not secure, if you are setting anything –much less a publicly served folder– to 777 you are leaving all the doors wide open. – Caleb Feb 28 '20 at 19:22
  • 1
    sounds very spooky)) **left to halt the servers – Vasilii Suricov Feb 28 '20 at 20:06
  • 1
    @VasiliiSuricov The correct solution involves several thing: the directory being _owned_ by the same user as the web server, having limited permissions, being marked as containing temporary files, making sure nothing uploaded can ever get mode 7 (execute, `+x`) permissions, and making sure to tell the web server nothing in there should be run (e.g. php files are only downloadable as raw content, not _run_ as code. – Caleb Feb 29 '20 at 09:11
  • This is good but umask cannot set the execute bits. – midnite Apr 15 '22 at 19:47
14

If you want to create the file not just empty but with content and mode at the same time you can use process substitution with install in bash:

install -m 755 <(echo commands go here) newscript

<() places the output into a temporary file (Process-Substitution)

laktak
  • 57,064
  • 17
  • 134
  • 164
7

You can create your own command:

create () {
    touch "$1"
    chmod "$2" "$1"
}

create filename.ext 644
Roberto Bonvallet
  • 31,943
  • 5
  • 40
  • 57
  • 1
    no no I think I was not able to place the query properly...it is not about a specific permission or about a specific file name....and the subroutine stuff is the snippet that I am using...I was just wondering whether there is a single command to do both the things together...ike for directories we have mkdir -m 777 dirname...likewise – user3075776 Jan 24 '14 at 21:19
5

The only reason your files are not created with 666 permissions right off of the bat is the umask. So if you disable the umask:

umask 0

then when you touch the file it will end up with permissions 666 automatically. It would not be a good idea to make text files executable usually. Directories would end up with 777 permission with umask disabled.

chicks@freecandy /tmp $ umask
0022
chicks@freecandy /tmp $ touch x
chicks@freecandy /tmp $ mkdir xx

chicks@freecandy /tmp $ umask 0
chicks@freecandy /tmp $ touch y
chicks@freecandy /tmp $ mkdir yy

chicks@freecandy /tmp $ ls -ld x xx y yy                                                                                  
-rw-r--r-- 1 chicks chicks  484 Jan 24 14:37 x
drwxr-xr-x 2 chicks chicks 4096 Jan 24 14:37 xx
-rw-rw-rw- 1 chicks chicks    0 Jan 24 14:37 y
drwxrwxrwx 2 chicks chicks 4096 Jan 24 14:37 yy
chicks
  • 2,393
  • 3
  • 24
  • 40
  • How do you change `umask` back to its former value afterward? – SherylHohman Jun 14 '19 at 17:26
  • 1
    Start a new shell before changing the umask, then you can just exit that shell to get the old umask back. https://unix.stackexchange.com/a/65890/79839 – chicks Jun 14 '19 at 18:26
3

touch filename.ext && chmod 777 $_

$_ is the most recent parameter

but as others have said 777 isn't a good idea

SDTbone
  • 49
  • 1
  • 3
  • 6
-1

Applying this to Docker

Because of Docker and Alpine, I imagine there are a lot of people interested in creating executable one-liners without the help of bash-isms. It's pretty simple with install

$ docker build -t temp - <<EOF
FROM alpine
RUN echo "date" | install -m 775 /dev/stdin /bin/now
CMD /bin/now
EOF

Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM alpine
 ---> d6e46aa2470d
Step 2/3 : RUN echo "date" | install -m 775 /dev/stdin /bin/now
 ---> Running in 95919b575638
Removing intermediate container 95919b575638
 ---> cd1fafd96ef3
Step 3/3 : CMD /bin/now
 ---> Running in 03b5c3ac7265
Removing intermediate container 03b5c3ac7265
 ---> 8f30b527dd29
Successfully built 8f30b527dd29
Successfully tagged temp:latest

$ docker run --rm temp
Thu Nov 12 07:44:24 UTC 2020

$ docker run --rm temp ls -la /bin/now
-rwxrwxr-x    1 root     root             5 Nov 12 07:44 /bin/now

$ docker rmi temp
Untagged: temp:latest
Deleted: sha256:8f30b527dd29203b67c86290b34b0a29d3c98a48134609d0b1e2b89087a7d6e7
Deleted: sha256:cd1fafd96ef3084226c5f98f472e3e08bc9a9f0448cc3e68b6f1c192d12d778a
Deleted: sha256:e58c75acbf953a64d35089cf50e1c5b762601e1cb9d9d1d668e135f23ce1fe86

Update

A commenter said:

I don't get what this has to do with the question.

  1. The OP's subject for this Q was "Single command to create a file and set its permission"
  2. The OP included "single command in unix (korn shell) that will do the two things together"

The subject causes this Q to come up for Docker users' Google searches. The OP specified "korn shell" which implies avoiding bash specific features. Because Alpine and many other minimal Linux bases used in containers does not include bash, the top ranked answer is not going to work for them. My answer does work for them and the OP's korn shell. Using Docker to demonstrate a solution means that anyone using Linux, macOS, Windows, or WSL2 in Windows can easily recreate the demo on their machine by reading the transcript from my machine.

E_net4
  • 27,810
  • 13
  • 101
  • 139
Bruno Bronosky
  • 66,273
  • 12
  • 162
  • 149
  • 1
    I don't get what this has to do with the question. – xeruf Apr 23 '21 at 07:54
  • This answer does add something useful - how to create a file with the given permissions and content piped from `stdin`. The other parts of this answer simply demonstrate that the technique works, so that is useful as well, though I think it could be made a bit clearer. – Ken Williams May 03 '22 at 15:48
  • Unfortunately this seems to not be portable to different OSes, for example on MacOS 12.3.1 I get the error `install: /dev/stdin: Inappropriate file type or format`. – Ken Williams May 03 '22 at 15:50
  • @KenWilliams that's really interesting. The host OS should not matter because the line `echo "date" | install -m 775 /dev/stdin /bin/now` is run inside an Alpine Linux container. I'll have to check if something chained in later versions of Alpine. Luckily I included a full transcript and can use the hash `d6e46aa2470d` to compare. – Bruno Bronosky May 03 '22 at 16:44
  • I should have clarified - when *not* running this in Alpine (Docker or otherwise), `install` isn't guaranteed to be able to deal with device files. It's too bad, because it would have been the perfect solution for my use case (content piped from an `echo`). – Ken Williams May 04 '22 at 20:46
  • @KenWilliams that makes more sense, but I'm still surprised based on the [macOS man page](https://ss64.com/osx/install.html#:~:text=Installing%20/dev/null%20creates%20an%20empty%20file) saying that `/dev/null` is a valid source. I no longer use a Mac, but I'll see if I can find a way to recreate your issue and find a solution. Thank you for the feedback. – Bruno Bronosky May 09 '22 at 16:56
  • @KenWilliams it's a common problem for macOS users: the `install` binary and many other core utilities (e.g., `grep`, `date`, `mktemp`, etc.) were **rewritten** from their AT&T Unix versions for GNU/Linux, and while an attempt was made to retain most behavior for their flags, there are distinct differences. Unix was adopted by BSD, and the Darwin kernel underlying macOS adopted FreeBSD's later flavor of the original Unix userland. – Ryder Dec 28 '22 at 18:07