ls -ltr using PHP exec() - php

as the problem states..
when i do
exec("ls -ltr > output.txt 2>&1",$result,$status);
its different from the normal output. An extra column gets added. something like
-rw-r--r-- 1 apache apache 211 Jul 1 15:52 withoutsudo.txt
-rw-r--r-- 1 apache apache 0 Jul 1 15:53 withsudo.txt
where as when executed from the command prompt its like
-rw-r--r-- 1 apache apache 211 2010-07-01 15:52 withoutsudo.txt
-rw-r--r-- 1 apache apache 274 2010-07-01 15:53 withsudo.txt
-rw-r--r-- 1 apache apache 346 2010-07-01 15:55 sudominusu.txt
-rw-r--r-- 1 apache apache 414 2010-07-01 15:58 sudominusu.txt
See the difference. So in the first output , my usual awk '{print $8}' fails.
I was facing the same problem with cron. But solved it by calling
./$HOME/.bashrc
in the script. But not happening using php. If somehow i can "tell" php to "exec" from the usual environment. Any help would be appreciated.

In your login shell, ls is probably aliased so that it prints another date. This would be in your .basrc or .bash_profile.
Explicitly pass the --time-style= option to ls to ensure that it prints the date in the expected format when using PHP.

I guess you are only interested in the file names and you want to sort with reverse time.
Try this:
ls -tr1 > output.txt 2>&1
You'll get a list with only the file names, so you don't need awk at all.
Another solution is to specify the time format with "--time-style iso". Have a look at the man page

That's not an extra output, that's a difference in formatting the date. Apparently you have a different locale set in PHP and in bash ("command prompt").
(in bash, running export LANG=C or export LANG=en_US gives the result with three-letter month name)

The output of ls is heavily dependent on the environment (e.g., LANG being the important variable here). Why not use a combination of scandir, stat, and krsort?
function ls($dir_name) {
$finfo = array();
foreach (scandir($dir_name) as $file_name) {
$s = stat(join('/', array($dir_name,$file_name)));
$finfo[$file_name] = $s['mtime'];
}
krsort($finfo);
return array_keys($finfo);
}
This will be safer and a lot more efficient than shelling out to ls. Not to mention that you get the benefit of being about to customize the sorting and filter the results in ways that are difficult to do inside of an exec.
BTW: I am by no means a PHP expert, so the above snippet is likely to be incredibly unsafe and full of errors.

Related

php shell_exec and exec doesn't work with shell script

So this is my code for the raspberry pi to get a still shot from the raspicam and save it on a directory,
<?php
exec('raspistill -n -hf -o /var/www/img/image.jpg --timeout 1');
?>
I have given the ownership and the permission to read/write in that forlder using -R. so my ls -al in /var/www is this
drwxr-xr-x 3 www-data www-data 4096 Jun 19 08:05 .
drwxr-xr-x 12 root root 4096 Jun 19 05:54 ..
-rwxrwxrwx 1 www-data www-data 74 Jun 19 08:30 getImg
drwxrwxrwx 2 www-data www-data 4096 Jun 19 09:21 img
-rw-r--r-- 1 root root 70 Jun 19 10:07 index.php
getImg is the script i tried to run the script as a file like shell_exec('/bin/bash ./getImg'); that also doesn't work.
i have added /bash/bin and tried to run the script without using the script file too but that doesn't get the results.
How ever when i try to run the php file in the terminal, it creates the image as it normally should. So i figure this must be a permission issue, but what else should i do with the permissions? I have given all the rights to the directory.
EDIT
So I have found a workaround to this. since I don't know what the cause for the problem, i'd not mark this as an answer, but please vote it to appear at the top.
I now execute the script using the cgi scripts. I have created a shell script in the /usr/lib/cgi-bin/
#!/bin/bash
echo "Content-type:text/html\n"
sudo raspistill -vf -n -o /var/www/img/image.jpg --timeout 1200 --metering matrix
echo "Status: 204"
I saved this as capture and made this executable, did nothing with the permissions though.
sudo chmod +x capture
now when i open the link http://192.168.1.85/cgi-bin/capture the browser will still get me a 500 internal server error message. how ever, the image would still be created.
I would now need to get the 500 internal server error to be fixed.
[I'd add this as a comment but don't have enough points for it]
if you use the optional parameters $output and $return_var to capture the output and return value what do you get?
string exec ( string $command [, array &$output [, int &$return_var ]] )
does your command rely on environment variables that may be available when you run it as your user but not as www-data? (you can use the env command to check that)
does it still work if you run it via terminal after switching user to www-data?

rsync in bash not parsing php-generated --exclude-from file

My rsync bash script isn't parsing my --exclude-from file that's generated via php, but it will if I manually create (as root) the same exact file locally. I've got a web interface on a Xubuntu 12.10 system that writes rsync --exclude-from files locally and then pushes them via rcp to our (CentOS 6) backup boxes that run the rsync script. (Please spare finger wagging about rcp... I know--don't have a choice in this case.)
Webpage writes file:
PHP:
file_put_contents($exclfile, $write_ex_val);
then pushes to the backup box from a local bash script on the webserver with:
Bash:
rcp -p /path/to/file/${servername}_${backupsource}.excl ${server}:/destination/path
I've compared the permissions and ownership of the hand-created file (that works) with the same file that's php/rcp'd (that doesn't), and they're both the same:
Bash:
stat -c '%a' server_backupsource_byhand.excl
644
stat -c '%a' server_backupsource_byphp.excl
644
ls -l server_backupsource_byhand.excl
-rw-r--r-- 1 root root 6 May 11 05:57 server_backupsource_byhand.excl
ls -l server_backupsource_byphp.excl
-rw-r--r-- 1 root root 6 May 11 05:58 server_backupsource_byphp.excl
In case it's relevant, here's my rsync line:
BASH:
rsync -vpaz -v --exclude-from=${exclfile} /mnt/${smbdir} /backup
I suspect php might be writing the file in a different format (e.g. UTF8 instead of ANSI), but I can't figure out how to test this, and have limited knowledge here.
Does anyone have any suggestions on how to get the php/rcp generated file to parse?
Using diff and file I found out that the php-written version wasn't writing the newline. In my variable definition I was using a "." as "$write_ex_val ." Removing this "." let php write the new lines. I also removed the space between the variable and "\n", although I'm not sure if this contributed to the solution. I'd upvote the comment, Earl, but I don't think I have enough rep. Thanks again.

exec("touch"); Not running in PHP

I need to modify the file date change using my php script. At my localhost, I used XAMPP running in Windows 7. I had no problem using PHP touch and is working properly as I want it to be.
Yet, when I uploaded it to my production, LINUX OS the PHP touch is not working anymore. I investigated it and found that, Linux, doesn't allow PHP touch or doesn't allow anybody to change file mod date.
That's why I use exec("touch filename.txt") instead and it working properly, but when I use this code
exec("touch -t 201204040000.00 filename.txt");
It doesn't do what it must, am I missing something here?
These are my references:
Linux / Unix Command: touch
3 UNIX / Linux touch Command Examples
EDIT
ls -l filename.txt
-rw-r--r-- 1 2012-11-04 12:00 filename.txt //supposed that 2012-11-04 12:00 is the original mod date of the file
If i run this code:
exec("touch filename.txt");
ls -l filename.txt
-rw-r--r-- 1 2012-11-05 11:00 filename.txt //supposed that 2012-11-05 11:00 is the current timestamp
The above code is working properly on me as everyone see.
But if i run it like this:
exec("touch -t 201204040000.00 filename.txt");
ls -l filename.txt
-rw-r--r-- 1 2012-11-05 11:00 filename.txt //The mod date doesn't changed at all.
I ran your command and it does exactly what it's supposed to do:
php -r 'exec("touch -t 201204040000.00 filename.txt");'
ls -l filename.txt
-rw-r--r-- 1 2012-04-04 00:00 filename.txt
maybe if you tell us what you expect we can help you more.

ls output changing when used through exec()

I'm using the ls command via PHP and exec() and I get a different output than when I run the same command via the shell. When running ls through PHP the year and month of the date get changed into the month name:
Running the command through the shell:
$ ls -lh /path/to/file
-rw-r--r-- 1 sysadmin sysadmin 36M 2011-05-18 13:25 file
Running the command via PHP:
<?php
exec("ls -lh /path/to/file", $output);
print_r($output);
/*
Array
(
[0] => -rw-r--r-- 1 sysadmin sysadmin 36M May 18 13:25 file
)
*/
Please note that:
-the issue doesn't occur when I run the PHP script via the cli (it only occurs when run through apache)
-I checked the source code of the page to make sure that what I was seeing was what I was getting (and I do get the month name instead of the proper date)
-I also run the ls command through the shell as the www-data user to see if ls was giving different output depending on the user (the output is the always the same from the shell, that is I get the date in yyyy-mm-dd instead of the month name)
Update with answer
alias was giving me this:
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
From those aliases I was unable to find a switch that was directly responsible for the time display:
-C list entries by columns
-F append indicator (one of */=>#|) to entries
-A do not list implied . and ..
-a do not ignore entries starting with .
-l use a long listing format
However using --time-style=long-iso in PHP did fix the issue.
ls has a couple command line options for date display format. check that your command line version isn't aliased to include something like ls --time-style=locale. The PHP exec'd version will most likely not have this aliasing present and is using default ls settings.
ls output depends from current locale settings. When you run it from console on behalf yourself it uses your locale settings, but user www-data has own locale settings (which probably differ from your). So, I suggest to you specify locale settings explicitly:
exec("LC_TIME=POSIX ls -lh /", $output);
where instead of POSIX you may substitute locale which you want to use.

Get filename from Unix "ls -la" command with regexp?

How can I produce a regular expressions pattern that returns the filename from any one of these lines? (I will search one line at a time).
drwxrwxrwx 4 apache apache 4096 Oct 14 09:40 .
drwxrwxrwx 11 apache apache 4096 Oct 13 11:33 ..
-rwxrwxrwx 1 apache apache 16507 Oct 17 10:16 .bash_history
-rwxrwxrwx 1 apache apache 33 Sep 1 09:36 .bash_logout
-rwxrwxrwx 1 apache apache 176 Sep 1 09:36 .bash_profile
-rwxrwxrwx 1 apache apache 124 Sep 1 09:36 .bashrc
-rwxrwxrwx 1 apache apache 515 Sep 1 09:36 .emacs
-rw------- 1 christoffer christoffer 11993 Sep 18 10:00 .mysql_history
drwxrwxrwx 3 apache apache 4096 Sep 1 09:48 .subversion
-rwxrwxrwx 1 christoffer christoffer 9204 Oct 14 09:40 .viminfo
drwxrwxrwx 14 apache apache 4096 Oct 12 07:39 www
The search is done using PHP, but I guess that doesn't really make a difference. :)
EDIT: The file listing is retrieved by a SSH connection and that is why I don't use a built in PHP-function. I need this full listing to see whether or not a file is actually a directory.
Try ls -a1F instead. That will list you all entries (-a), one per line (-1), with additional information about the file type appended to the name (-F).
You will then probably get something like this for your directory:
./
../
.bash_history
.bash_logout
.bash_profile
.bashrc
.emacs
.mysql_history
.subversion/
.viminfo
www/
The directories have a slash / at the end.
The main question is... Why? Use readdir and stat instead.
<?php
$directory = './';
$dh = opendir($directory);
while (($file = readdir($dh)) !== false)
{
$stat = stat($directory.$file);
echo '<b>'.$directory.$file.':</b><br/>';
var_dump($stat);
}
If you are looking for directories, rather than parsing ls output, just use find.
find -maxdepth 1 -mindepth 1 -type d
This will list the directories like this:
./Documents
./.gnupg
./Download
You no longer have to parse the data to determine what is a directory and what isn't.
If you're actually wanting the files, and not the directories, you use -type f instead.
Your parsing of the ls output may very well break on symlinks...
I wouldn't use regex
Given a line, you could explode and pop the last element from the array
if (preg_match('/^d/', $line)) {
$name = array_pop(explode(' ', $line));
}
EDIT: none of your examples have embedded spaces but a later comment suggests that it IS important to find filenames
Adding to what Matthew said, there's plenty of reasons to not parse ls output. You might have spaces in file names - or even delete characters. The format of the date part of the listing, especially for older files, is different, the size of the large files can break the listing.
If you must use regex, and you really have no spaces in file names, then just tie to the end of the line and get the non-spaces you find there
(\S+)$
There's a nicer way to do this in php5 using the spl and DirectoryIterator
$dir = '.';
foreach (new DirectoryIterator($dir) as $fileInfo) {
echo $fileInfo->getFilename() . "<br>\n";
}
Given your constraint of using the full directory listing I would do it this way:
ls -l | egrep '^d' | awk '{print $NF}'
Egrep command would search for the letter "d" at the beginning of the line. Awk by default uses spaces as seperators and the $NF will get you the last element. The only edge case I can think of where this wouldn't always work 100% of the time is when the file name would have spaces in it.
I would suggest using the find command:
find . -maxdepth 1 -type d | awk -F '/' '{print $NF}'
The find command above will get you only the files/directories in your current directory (b/c of -maxdepth 1 arg). The awk command will split the line using the '/' and will only retrieve the last token ($NF).
Because, the awk command
awk -F '/' '{print $NF}'
will get you the last element you can essentially use:
find . -maxdepth x -type d
where x is a number of your choice >= 1, you'll still get what you want, the filename and/or the directory name.
\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)
Each string is built of 9 parts separated by whitespace. You are looking for the 9th part.
Use glob('*') instead?
Instead of trying to parse difficult output, how about generating some more helpful output in the first place. For example:
ssh user#machine 'cd /etc; for a in *; do [ -f "$a" ] && echo "$a"; done'
will generate a list of non-directory files in /etc on the remote machine. This should be much easier for you to parse.
Displays hidden files too, try it if you don't believe me.
glob('{,.}*', GLOB_BRACE);

Categories