The Electric Toolbox Blog

Linux, Apache, Nginx, MySQL, Javascript and PHP articles

The Electric Toolbox Blog has articles about Linux, Apache, Nginx, MySQL, Javascript and PHP.

The ten most recent articles can be found below in their entirety. Navigating the sections in the right navigation (under Categories) will bring up all the other posts, and you can also use the search box at the top of the page to find what you might be looking for.

Showing seconds with ls on Linux and OSX

Posted Mar. 31, 2016 in Linux/Unix/BSD and OSX

Using the "ls" command to list directory contents from the command line with the -l flag will include the date and time down to the minute, but not show the seconds. This post shows how to display the seconds as well with bash on Linux and OSX.

On Linux

Use one of the following, the second one effectively being a shorter version / synonym of the first one:

ls -l --time-style=full-iso
ls --full-time

The output of either of the above will be this:

total 0
-rw-r--r-- 1 chris chris 0 2016-03-31 12:01:10.000000000 +1300 foo.txt

If you don't want the less than a second and timezone offset part, you can format the output yourself, e.g. like this:

ls -l --time-style=+"%Y-%m-%d %H:%M:%S"

The output of this is:

total 0
-rw-r--r-- 1 chris chris 0 2016-03-31 12:01:10 foo.txt

On OSX

None of the above will work on OSX, but there's a flag available on OSX which isn't on Linux which makes it all a lot easier: -T which you need to use with -l, for example:

ls -lT

And example output:

total 0
-rw-r--r--  1 chris  staff  0 31 Mar 12:01:10 2016 foo.txt

Versions used

The above examples were using:

Bash 4.2.37 on Debian 7.9 Wheezy

Bash 3.2.57 on OSX 10.10.5 Yosemite

Show only one process with top on Linux

Posted Mar. 10, 2016 in Linux/Unix/BSD

Top is a useful command line tool for showing processes running on Linux (and other operating systems) such as how much CPU and memory they've been using and how long they've been running, and also showing the system load, CPU and memory usage as a whole. But what if you only one to show output for one process or command?

Use the -p flag to specify the pid

If you know the pid number of the process, use the -p flag to specify it:

top -p [pid]

e.g.

top -p 1234

Combine with pidof if you don't know the pid

pidof will return the PIDs for the process that matches the input, so you can do this:

top -p `pidof [process name]`

The only problem with this is that if there's more than one process matching the name then it won't work, because you either have to specify each pid with another -p flag or comma separate the values, and you'll get an error like this, when trying to do top for just nginx:

$ top -p `pidof nginx`
	top: unknown option '1'
usage:	top -hv | -bcHiSs -d delay -n limit -u|U user | -p pid[,pid] -w [cols]

You can either use pidof's -s flag to return just one pid:

top -p `pidof -s nginx`

which isn't ideal, because you're only get one of them, or combine it with some sed magic to convert spaces between the ids returned from pidof with commas:

top -p `pidof [process name] | sed 's/ /,/g'`

Replace [process name] with the process, e.g. with nginx as shown in the next example:

top -p `pidof nginx | sed 's/ /,/g'`

Combine with pgrep to match a name

Another alternative to combining top with pidof is to use pgrep. This can be useful if you need to match e.g. a PHP script which is running from the command line and which you could only match 'php' when running pidof and might return something different from what you want.

(Note that there is a -x flag for pidof which the man page says "Scripts too - this causes the program to also return process id's of shells running the named scripts" but it didn't seem to work when I tried it myself.)

If the script is called e.g. "myscript.php" you can do this:

top -p `pgrep -f myscript.php`

Again this will cause an error if it returns more than one pid. The output from pgrep will be have each pid on a new line, so we need to use tr to replace the newlines with commas and then sed to remove the final comma. If anyone has a tidier way of doing this, then please let me know in the comments.

top -p `pgrep -f myscript.php | tr '\n' , | sed s/,$//`
top -p `pgrep -f nginx | tr '\n' , | sed s/,$//`

And because pgrep does regular expression pattern matching, you can do all sorts of things with it, but note that there's a limit of 20 pids.

top: pid limit (20) exceeded

Oops, you can't pass any more than 20 pids to top.

You could combine the output of pidof or pgrep with "head -n 20"...

Change the commit message with git

Posted Feb. 29, 2016 in Linux/Unix/BSD and Offsite Articles

I'm sure you've done it before: added a commit message with git but typed it in wrong and need to change it. If you haven't pushed the commit yet then it's as simple as 'git commit --amend -m "New commit message"'.

Quick answer

If you haven't yet pushed the commit, use this to edit it in a text editor:

git commit --amend

Use this to do the whole thing from the command line:

git commit --amend -m "New commit message"

The full answer / more answers

I need a quick reference link to a comprehensive answer myself, hence this quick post. Several useful answers and examples covering a variety of situations is here at Stack Overflow.

Possible responses from Sendy's subscribe API call

Posted Feb. 25, 2016 in Sendy

Sendy is a self-hosted email marketing tool; while hooking up a subscription form, I needed to work out what all the possible responses are to a user subscribing, depending on their current status in the Sendy database.

Version

The version documented here is 2.0.7. The previous version had a bug in it which prevented the 'Already subscribed.' status from being returned as plain text and it would always return a full HTML page. If you are using an older version, you should upgrade.

Example API call using PHP

The following example calls the subscribe API function. Replace /path/to/sendy with the full domain and path to your Sendy install, and obviously the other fields too. The 'boolean' value in the data array is set to a text value of 'true' to return the response as plain text.

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, '/path/to/sendy/subscribe');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
	'name' => '<name here>',
	'email' => '<email address here>',
	'list' => '<list id here>,
	'boolean' => 'true'
));
$response = curl_exec($ch);
curl_close($ch);

Possible responses, when double opt in is enabled

Note, you can also get other error messages such as 'Some fields are missing.' but these reponses assume all fields are completed and the email address is valid.

  • User is already active in Sendy: API returns 'Already subscribed.'
  • User is not yet in Sendy: an opt in email is sent and the API returns '1'
  • User has not yet confirmed the double opt in: another opt in email is sent, and the API returns '1'
  • User has previously unsubscribed from Sendy: the opt in email is NOT sent, the user is set back to subscribed, and the API returns '1'
  • User has been marked as soft bounced: the opt in email is NOT sent, there is no status change, and the API returns 'Already subscribed.'
  • User has been marked as hard bounced: the opt in email is NOT sent, there is no status change, and the API returns 'Already subscribed.'
  • User has marked one of your email messages as spam: the opt in email is NOT sent, there is no status change, and the API returns 'Already subscribed.'

Where did "Save for Web" go in Adobe Photoshop?

Posted Feb. 23, 2016 in Applications

Save for Web in Adobe Photoshop CC 2015 was moved on the menus from File -> Save for Web to File -> Export -> Save for Web (Legacy).

Where did it go?

It moved from:

File -> Save for Web

to:

File -> Export -> Save for Web (Legacy)

From the horse's mouth

Here's the official bog notice from Adobe about it: "Save for Web in Photoshop CC 2015".

It notes that "Save for Web will not be removed without replacing its capabilities in a new and improved workflow."

I'm not much of a menu user, and use the somewhat awkward Cmd+Option+Shift+C keyboard shortcut so had never even noticed the change. My wife, on the other hand, uses menus and not keyboard shortcuts, and that extra step through the menu makes the current workflow much more awkward.

Solution 1: use shortcut keys

The first solution to speed things up is to use the shortcut keys. Cmd+Option+Shift+C is the default, but you can always change the shortcuts under Edit -> Keyboard Shortcuts. My own personal preference is to try to avoid changing shortcut keys so that I get used to them in case I need to use someone else's computer for something.

Solution 2: install the "Export Helper" add on

Yep, save yourself navigating through the menus / using an awkward keyboard shortcut by installing an add on... mind you, once you have the panel activated you now have one-click saving.

Go to the Photoshop Export Helper page to install it.

You need to have Creative Cloud file sync on, and it will automatically download it. Restart Photoshop and it will be under Window -> Extensions. When you click it, the panel will appear, but you'll probably need to drag it onto another group of panels otherwise I found it tended to disappear or be hidden or something.

To make sure Creative Cloud's file sync is on, go to Creative Cloud, click the cog, select "Preferences", then "Creative Cloud" then "Files" and the sync function is there. The screenshots below illustrate how to get there on a Mac; sorry, I can't help with Windows.

creative cloud file sync

creative cloud file sync

View the message header and body for an email in the exim mail queue

Posted Feb. 12, 2016 in Email Servers -

You've checked to see what's sitting in the exim mail queue, but want to check the email's header and/or body before deleting it from the queue. Use the -Mvh & -Mvb flags to view these.

Getting the message id

Use "exim -bp" or "mailq" to see what's in the queue to get the message id; you'll need it when you want to view the header or body.

$ exim -bp
 1m   321 1aU276-0001cf-DR 
          chris@example.com

In the above example, 1aU276-0001cf-DR is the message id

Viewing the email header with exim

Run "exim -Mvh [message id]" to see the headers ("h" for header), e.g. using the id above:

$ exim -Mvh 1aU276-0001cf-DR

Viewing the email body with exim

Run "exim -Mvb [message id]" to see the headers ("b" for body), e.g. using the id above:

$ exim -Mvb 1aU276-0001cf-DR

RamNode cheap virtual servers

Posted Feb. 06, 2016 in Linux/Unix/BSD

I needed a really cheap standalone virtual server to test some stuff, on the internet and not running behind a VDSL router at my office, and got the cheapest RamNode server for $13.50 for a whole year.

RamNode servers

I honestly have no idea if RamNode are any good or not (my own websites are hosted using VPS servers at Linode), but for what I need this is perfect: it's more or less throw away money, and I get a server with a static IP address I can switch on, test some stuff and switch back off again as and when needed.

What am I testing?

I'm testing out the free Let's Encrypt secure certificates. These are domain validated certificates: validation is done by a certificate management agent on the web server, which receives requests from the LE servers to prove you manage the domain. I'll talk more about this in a future post, but suffice to say for the moment that I didn't want to run this on a production server (yet).

Get it here

Their cheapest one has 128MB of RAM, 12GB of storage and 500GB of bandwidth, running OpenVZ - for $15 for a year. You can get a 10% discount using the SSD10 coupon code. You can order it here and/or check out the other plans.

I am not affiliated with RamNode in any way, and the above link is not an affiliate link. I found out about them from the comments in this article at Ars Technica.

nslookup: command not found error on Debian

Posted Feb. 05, 2016 in Linux/Unix/BSD

If you need to use nslookup on a Debian server and you're getting an "nslookup: command not found" then use apt-get to install dnsutils.

Error message

You'll know if dnsutils & nslookup are not installed if you try a dns lookup and get this error:

$ nslookup www.example.com
-bash: nslookup: command not found

Install dnsutils

Install the dnsutils package to resolve the error and lookup the domain:

$ apt-get install dnsutils

And now it works

$ nslookup www.example.com
Server:		8.8.8.8
Address:	8.8.8.8#53

Non-authoritative answer:
Name:	www.example.com
Address: 93.184.216.34

Base64 decode from the command line

Posted Dec. 17, 2015 in Linux/Unix/BSD

I have some automated processes that receive updates via email attachments which are base64 encoded. The entire email is backed up into a text file, and very occasionally I need to check what was in an attachment. The command line base64 tool can help with this, either decoding a file or standard input.

Using base64 to decode a file

The -d or --decode flag tells base64 it's decoding data (on a Mac -d is a debugging flag, so it's -D and --decode instead). In the examples below, I've used --decode, but you can use -d or -D instead.

To decode the file at /tmp/encoded.txt do this:

base64 --decode /tmp/encoded.txt

And the decoded file will be written to standard output. If you wanted to write it to a file, you can redirect it like this, where it's written out to /tmp/decoded.txt

base64 --decode /tmp/encoded.txt > /tmp/decoded.txt

Using base64 to decode some text

If you run base64 --decode without a file, you can type text (or copy and paste it), hit return/enter, and then control+d / ctrl+d and it will be decoded.

So, for example, to decode VGhpcyBpcyBiYXNlNjQgZW5jb2RlZAo=, which is "This is base64 encoded" encoded as base64, do this:

base64 --decode
VGhpcyBpcyBiYXNlNjQgZW5jb2RlZAo=
[ctrl+d]

It will then be written to standard output. Again, you can redirect the output to a file:

base64 --decode > /tmp/decoded.txt
VGhpcyBpcyBiYXNlNjQgZW5jb2RlZAo=
[ctrl+d]

Using pbpaste on a Mac

If you have the encoded text in your clipboard, you can use pbpaste to pipe it through base64 instead of having to manually paste in the terminal. There are equivalent commands when using X (xclip) but I've never used these myself.

Using pbpaste:

pbpaste | base64 --decode

Install Java on the command line with Debian

Posted Dec. 16, 2015 in Linux/Unix/BSD

If it was nice and intuitive to install Java on Debian (and other Debian based distros such as Ubuntu), I wouldn't need to write this post because it would just be "apt-get install java". But no, I always forget what I need to install, hence this post...

Install openjdk-7-jre or openjdk-7-jre-headless

If you just want to install the runtime so you can run Java applications, install either openjdk-7-jre or openjdk-7-jre-headless. The first one will install a bunch of Gnome stuff if not already installed, so if you are doing this on a server without a GUI then install openjdk-7-jre-headless.

sudo apt-get install openjdk-7-jre

OR

sudo apt-get install openjdk-7-jre-headless

Right, now maybe I'll remember this next time :)

This is correct for (at least) Debian 7 & 8, possibly older versions, probably future versions, and possibly other Debian based distributions.