8

I'm trying to run a command, write that to a file, and then I'm using that file for something else.

The gist of what I need is:

myAPICommand.exe parameters > myFile.txt

The problem is that myAPICommand.exe fails a lot. I attempt to fix some of the problems and rerun, but I get hit with "cannot overwrite existing file". I have to run a separate rm command to cleanup the blank myFile.txt and then rerun myAPICommand.exe.

It's not the most egregious problem, but it is annoying.

How can I avoid writing a blank file when my base command fails?

goodguy5
  • 194
  • 1
    How is failure indicated? A return code? An empty file? – Jeff Schaller Dec 17 '18 at 14:52
  • @JeffSchaller error is written to the command line, in this case. – goodguy5 Dec 17 '18 at 18:33
  • "command-line" meaning redirected to myFile.txt as "stdout", or to the screen in this case, as "stderr"? – Jeff Schaller Dec 17 '18 at 18:34
  • @JeffSchaller, I'm sorry. stderr – goodguy5 Dec 17 '18 at 18:57
  • 4
    Sorry, but you're having it completely backwards; you should redirect the output to a temporary file, and rename the temp file to the final destination if your command succeeds. t=$(mktemp); trap 'rm -f "$t"' EXIT INT TERM; the_cmd >"$t" && mv "$t" the_file. That way, the output file will always contain valid data irrespective on whether the command succeeded or failed. –  Dec 18 '18 at 00:53

3 Answers3

11

You can delete the file after running, if the command fails, with

myAPICommand parameters > myFile.txt || rm myFile.txt

But I would suggest clobbering the file instead:

myAPICommand parameters >| myFile.txt

See What are the shell's control and redirection operators? for details.

Stephen Kitt
  • 434,908
11

You must have "noclobber" set, check the following example:

$ echo 1 > 1  # create file
$ cat 1
1
$ echo 2 > 1  # overwrite file
$ cat 1
2
$ set -o noclobber
$ echo 3 > 1  # file is now protected from accidental overwrite
bash: 1: cannot overwrite existing file
$ cat 1
2
$ echo 3 >| 1  # temporary allow overwrite
$ cat 1
3
$ echo 4 > 1
bash: 1: cannot overwrite existing file
$ cat 1
3
$ set +o noclobber
$ echo 4 > 1
$ cat 1
4

"noclobber" is only for overwrite, you can still append though:

$ echo 4 > 1
bash: 1: cannot overwrite existing file
$ echo 4 >> 1

To check if you have that flag set you can type echo $- and see if you have C flag set (or set -o |grep clobber).

Q: How can I avoid writing a blank file when my base command fails?

Any requirements? You could just simply store the output in a variable and then check if it is empty. Check the following example (note that the way you check the variable needs fine adjusting to your needs, in the example I didn't quote it or use anything like ${cmd_output+x} which checks if variable is set, to avoid writing a file containing whitespaces only.

$ cmd_output=$(echo)
$ test $cmd_output && echo yes || echo no
no
$ cmd_output=$(echo -e '\n\n\n')
$ test $cmd_output && echo yes || echo no
no
$ cmd_output=$(echo -e ' ')
$ test $cmd_output && echo yes || echo no
no
$ cmd_output=$(echo -e 'something')
$ test $cmd_output && echo yes || echo no
yes

$ cmd_output=$(myAPICommand.exe parameters)
$ test $cmd_output && echo "$cmd_output" > myFile.txt

Example without using a single variable holding the whole output:

log() { while read data; do echo "$data" >> myFile.txt; done; }
myAPICommand.exe parameters |log
Evolter
  • 161
  • 1
    I like this answer and will likely accept it eventually. While it fixes my problem, it doesn't actually answer the question. – goodguy5 Dec 17 '18 at 14:59
  • I have updated my answer if that helps. – Evolter Dec 17 '18 at 15:52
  • lovely. Thank you. I didn't think of shortcircuit logic. – goodguy5 Dec 17 '18 at 16:10
  • 2
    I think you need to quote $cmd_output otherwise you get unexpected results if $cmd_output is (say) 1 == 2. If you want to protect against "$cmd_output" being only whitespace, I would consider piping it into grep -q with a suitable pattern. – Ben Millwood Dec 17 '18 at 16:35
  • I agree that it isn't bulletproof example @BenMillwood it just suppose to give an idea. With more requirements (different shells handle variables a bit differently, etc.) we could make it better :) – Evolter Dec 17 '18 at 16:50
  • Storing the output in a variable could become a problem if the output is large. – kasperd Dec 17 '18 at 21:35
  • @kasperd there are planty of other ways - all depends on the requirements. For example pipe output to a function that will read the output by line and save it to a file or does nothing otherwise, log() { while read data; do echo "$data" >> myFile.txt; done; } ; myAPICommand.exe parameters |log. You can as well look for some dedicated to logging packages or even use systemd logging :) – Evolter Dec 18 '18 at 00:06
3

You could create a script to run the myAPICommand.exe, but have it first remove the myFile.txt if it exists. Then you don't have to constantly do the rm command to clean up.

Like:

if [ -e myFile.txt ]
then
    rm myFile.txt && myAPICommand.exe
else

You could also make it so that your command cleans up after itself. If the file is empty adding something like the following.

Like:

if [ -s myFile.txt ]
then
        EXIT 0
else
        rm myFile.txt && EXIT 1
fi
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255