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:


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:


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?


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
  • @StephenKitt echo $drt outputs /var/www/html. – user9303970 Apr 6 '18 at 4:24
  • 1
    Looks like you're just missing echo in front of rm. – Kusalananda Apr 6 '18 at 5:43
  • @Kusalananda while you wrote this comment I just finished writing an answer about it. – user9303970 Apr 6 '18 at 5:44

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).

  • @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. – Eliah Kagan Apr 6 '18 at 8:36

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.


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.

  • 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). – Eliah Kagan Apr 6 '18 at 8:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.