2

In Ubuntu 16.04 xenial with Bash 4.3.48 and LEMP, I've created a comfortability function to create a php info file (to better know my php environment), and then delete it after 2 hours. but I'm having a problem calling that function from terminal.

Steps to reproduce my problem

1) Create a file with a function:

#!/bin/bash drt="/var/www/html" pif() { cat > "$drt"/info.php <<< "<?php phpinfo();" rm "$drt"/info.php | at now + 2 hours } 

2) Source that file.

3) Call the function in terminal:

pif 

Sadly, no file by the name info.php was created for me in /var/www/html. I double checked that.

Here's the full trace for executing the function this way (pif):

+ pif + cat + at now + 2 hours + rm /var/www/html/info.php warning: commands will be executed using /bin/sh job 17 at Fri Apr 6 05:43:00 2018 

On the other hand, when I try to create the file not from a function, just by executing:

cat > "$drt"/info.php <<< "<?php phpinfo();" 

The file is created fine.

My question

Why did direct execution from terminal worked but execution of a function containing the code failed?


Update

1) I do indeed have write permissions to /var/www/html.

2) I sourced the file by:

source myFunctions.sh 

3) The output of type -a pif is:

pif is a function pif () { cat > "$drt"/info.php <<< "<?php phpinfo();"; rm "$drt"/info.php | at now + 2 hours } 
3
  • @StephenKitt echo $drt outputs /var/www/html. Commented Apr 6, 2018 at 4:24
  • 1
    Looks like you're just missing echo in front of rm. Commented Apr 6, 2018 at 5:43
  • @Kusalananda while you wrote this comment I just finished writing an answer about it. Commented Apr 6, 2018 at 5:44

2 Answers 2

3

Your function creates info.php but then immediately deletes it.

After creating the file info.php, it seems like you intend for the next command to schedule its deletion:

rm "$drt"/info.php | at now + 2 hours 

But that's not what is happening. That runs the rm command right now, then pipes its standard output (to which nothing would typically be written, as rm succeeds silently by default) to the at command's standard input.

If you really need to have that shell function schedule the file's deletion, then you should provide the command as text on at's standard input, or schedule a command that runs a separate script that deletes the file. See at(1) for details about how to schedule commands to run with at.

Note that you can actually see, from your trace, that this is what was happening:

+ at now + 2 hours + rm /var/www/html/info.php 

Commands in a pipeline run asynchronously. That trace showed that the shell behaved as it should when you give it two commands separated by |. It ran the at command that appeared on the right side of the pipe so that it could receive data piped from the rm command. Then it immediately ran the rm command. When it did so, the rm command removed the file. To prevent this, you must tell at to run that command later, not run it immediately in a pipeline yourself.

If you want to use something that resembles what you have, and provided that the value of drt (which is /var/www/html in your example) does not contain any spaces or other characters that will be treated specially by /bin/sh (which at uses to run the command), then you can use:

at now + 2 hours <<<"rm -- $drt/info.php" 

If the value of drt is known not to start with a -, then the -- argument may be safely omitted. In your situation, where you appear to be in control of $drt, this seems like a pretty safe bet.

You may find it convenient to test out your at commands by scheduling them to run at some sooner time, such as immediately (at now) or in a minute (at now + 1 minute).

1
  • @user9303970 It seems to me that there might be a simpler way than at, but I'm not totally sure about your requirements. For example, perhaps you could avoid scheduling the deletion of the file, and instead just delete it when you no longer need it. I'm not asserting that this would be suitable for your specific needs, though. Commented Apr 6, 2018 at 8:36
1

I have accepted Eliah's answer above and I think it should be thumbed up, but here is the explanation I created to myself, regarding a bit different code I particularly used.

Why the above code was wrong:

It seems that my command failed due to lack of echo.

Consider my original command:

rm "$drt"/info.php | at now + 2 hours 

What happened is that the rm ran directly after creating the file, and only then, an empty at ran.

In an even simpler diagram:

1) Create a phpinfo with an here-string.

2) Delete phpinfo with rm (nothing is further piped anywhere).

3) Wait 2 hours (for nothing, in this case).


A correct code

What I should have done (and that works fine and I've double checked that), is this:

echo "rm $drt/info.php" | at now + 2 hours 

The diagram for this command is this:

1) Create a phpinfo with an here-string.

2) Echo an rm command but pipe the output of the echo into the following (at) command.

3) rmat now + 2 hours.

Conclusion

When using code similar to mine, ensure to wrap the command you want to schedule with at with echo "", if you work in the paradigm of do X at now + y time (from now), per the code example above.

1
  • Note that it's not always trivial to wrap the command. echo "rm $drt/info.php" | at now + 2 hours and at now + 2 hours <<<"rm $drt/info.php" work the same and have the same limitations. (I assume you run them from bash, where both are reasonable; bash has <<< here-string syntax, and bash's echo builtin doesn't expand backslash escapes.) If $drt is /a/b c, at runs rm /a/b c/info.php, which would try to delete /a/b and separately c/info.php. The " " around the command used with echo or <<< are not preserved (and if they were, it would not always work as you wanted). Commented Apr 6, 2018 at 8:33

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.