Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe shell redirection when command not found

Tags:

bash

shell

Let's say we have a text file named text (doesn't matter what it contains) in current directory, when I run the command (in Ubuntu 14.04, bash version 4.3.11):

nocommand > text # make sure noommand doesn't exists in your system

It reports a 'command not found' error and it erases the text file! I just wonder if I can avoid the clobber of the file if the command doesn't exist. I try this command set -o noclobber but the same problem happens if I run:

nocommand >| text # make sure noommand doesn't exists in your system

It seems that bash redirects output before looking for specific command to run. Can anyone give me some advices how to avoid this?

like image 564
zhujs Avatar asked Dec 20 '22 12:12

zhujs


2 Answers

Actually, the shell first looks at the redirection and creates the file. It evaluates the command after that.

Thus what happens exactly is: Because it's a > redirection, it first replaces the file with an empty file, then evaluates a command which does not exist, which produces an error message on stderr and nothing on stdout. It then stores stdout in this file (which is nothing so the file remains empty).

I agree with Nitesh that you simply need to check if the command exists first, but according to this thread, you should avoid using which. I think a good starting point would be to check at the beginning of your script that you can run all the required functions (see the thread, 3 solutions), and abort the script otherwise.

like image 169
Emilien Avatar answered Jan 04 '23 06:01

Emilien


Write to a temporary file first, and only move it into place over the desired file if the command succeeds.

nocommand > tmp.txt && mv tmp.txt text

This avoids errors not only when nocommand doesn't exist, but also when an existing command exits before it can finish writing its output, so you don't overwrite text with incomplete data.

With a little more work, you can clean up the temp file in the event of an error.

{  nocommand > tmp.txt || { rm tmp.txt; false; } } && mv tmp.txt text

The inner command group ensures that the exit status of the outer command group is non-zero so that even if the rm succeeds, the mv command is not triggered.

A simpler command that carries the slight risk of removing the temp file when nocommand succeeds but the mv fails is

nocommand > tmp.txt && mv tmp.txt text || rm tmp.txt
like image 25
chepner Avatar answered Jan 04 '23 07:01

chepner