PATH environment variable in sudo

Using sudo is a common, safe and recommended method to execute commands that require superuser privileges. However, this command resets the PATH environment variable. So, some badly written installation scripts that require a particular PATH will fail in strange ways when run as sudo.

Here is some useful information about sudo and the PATH environment variable:

  • To ensure safety, sudo by default does not use the PATH environment variable of the user or that of root.

  • sudo also ignores the system-wide environment variables set in /etc/environment or in /etc/profile.d/*.sh.

  • The PATH variable for sudo is hardcoded to /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

  • If you really want sudo to pick up the system-wide PATH or other environment variables, then try this:

    1. Set the PATH or environment variable in a new file named /etc/profile.d/name_anything.sh using export
    2. Start root shell using sudo su -
    3. Check if your path is correct: echo $PATH
    4. Run the command that requires superuser privilege.
  • Reference:

Tried with: Ubuntu 14.04

Environment variables in Fish

Environment variables can be set just like any other variable in the Fish shell.

  • To export an environment variable to the shell, add it in ~/.config/fish/config.fish:
set -x FOO_ENV_VAR /home/joe/bin/some_foo_dir
  • If this a environment variable with many values, then set it as an array. In Fish, you set an array variable with a space-delimited series of values:
set -x FOO_ENV_VAR /home/joe/bin/some_foo_dir /usr/local/foo_dir

Under Bash, you might have set this same environment variable in ~/.bashrc as:

export FOO_ENV_VAR=/home/joe/bin/some_foo_dir:/usr/local/foo_dir
  • If the environment variable with many values already exists and you want to prepend or append a value to it, the technique is the same. For example, to prepend to PATH:
set -x PATH /home/joe/bin/some_foo_dir $PATH
  • Three special environment variables: PATH, CDPATH and MANPATH are treated differently by Fish. These arrive from the environment to Fish as colon-separated, they are converted to array so we can set them easily as shown above using space-delimiter and then they are sent back to the environment as colon-separated. This logic can be seen in the Fish source code file src/env.cpp here. This is also explained in this issue.

  • The problem now is that there are many other environment variables which need to be colon-delimited, but Fish does not do that. For example, the dynamic linker ld.so that is used to load up DLLs when a binary executes requires LD_LIBRARY_PATH environment variable to be colon-delimited. I got errors when this variable was set using space-delimiters: cannot open shared object file: No such file or directory

  • If you run into any problems with an environment variable that takes a series of values, then check back to see how it is set in traditional shells like Bash. If it is colon-delimited there, then you might need to make it colon-delimited in Fish too. For example:

set -x FOO_ENV_VAR /home/joe/bin/some_foo_dir:$FOO_ENV_VAR

Tried with: Fish 2.2 and Ubuntu 14.04

Autojump path repeats in PATH in Fish

Problem

Autojump can be installed and used with Fish shell as described here. However, if you source your Fish config file repeatedly, then the autojump path appears repeated in your PATH environment variable:

$ echo $PATH
/home/joe/.autojump/bin /home/joe/bin /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin
$ source ~/.config/fish/config.fish
$ echo $PATH
/home/joe/.autojump/bin /home/joe/.autojump/bin /home/joe/bin /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin

Solution

I have a commit here that fixes this behavior. This pull request hopefully be merged into Autojump and if you get the latest version of Autojump you should no longer face this problem.

Tried with: Autojump 22.2.4, Fish 2.2 and Ubuntu 14.04

Pip install error with PyCUDA

Problem

  • I tried to install PyCUDA using pip:
$ sudo pip install pycuda
  • The installation tries to compile a few C++ files and it failed on the very first file with this error:
In file included from src/cpp/cuda.cpp:1:0:
src/cpp/cuda.hpp:14:18: fatal error: cuda.h: No such file or directory
#include <cuda.h>
                ^
compilation terminated.
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

Investigation

  • This error was strange because I had set CUDA_ROOT and had added the bin path of CUDA installation to PATH environment variable. So, the installer should have found cuda.h which I could see was present in $CUDA_ROOT/include

  • To see what was happening, I tried the same command with verbosity:

$ sudo pip -vvv install pycuda
  • Now I could see that it was failing to find nvcc.

  • On downloading the source code of PyCUDA and checking setup.py, I saw that the check for nvcc was used to figure out the CUDA_ROOT and CUDA_INC_DIR.

  • The reason nvcc was not visible was that CUDA_ROOT was set for my user, but this PATH is not visible when a command is run under sudo, as described here. The solution was to make the CUDA bin path visible to sudo.

Solution

To make the $CUDA_ROOT/bin available in PATH for sudo, we can follow the steps described here. For example, on my system with CUDA 7.0 I followed these steps:

  • Created a new file /etc/profile.d/cuda.sh and added this line:
export PATH=/usr/local/cuda-7.0/bin:$PATH
  • Opened root shell without resetting PATH and ran the pip installation:
$ sudo su -
$ pip install pycuda

This worked and PyCUDA was installed successfully! :-)

Tried with: PyCUDA 2015.1.2, CUDA 7.0 and Ubuntu 14.04

USBView on Linux

20150730_usbview

You can list your USB devices using lsusb. However, the USB subsystem of controllers, hubs and devices connected to them has the structure of a tree and it would be most convenient to view as such. USBView is a small GTK tool written by kernel developer Greg Kroah-Hartman that shows the topography of the devices connected to your USB subsystem in this tree format.

  • Installing USBView is easy:
$ sudo apt install usbview
  • This tool parses the information available from /sys/kernel/debug/usb/devices and presents it nicely. Reading this file needs superuser permissions. You can read this information manually too:
$ sudo cat /sys/kernel/debug/usb/devices
  • To invoke USBView:
$ sudo usbview
  • In the tree presented by USBView, click on the controllers, hubs and devices connected to them to view information about them.

  • Some of the information I typically look for:

    • Manufacturer
    • Serial Number
    • Speed
    • USB version (2 or 3)
    • Device class
    • Power needed (in mA)

Tried with: USBView 2.0, Linux 3.13.0-52-generic and Ubuntu 14.04

How to change DPI in Matplotlib

Matplotlib can be used to output plot to PNG image files. To render for this bitmap format, Matplotlib uses a default DPI of 100. This is a surprisingly low DPI for plots. Thankfully, the DPI can be changed easily.

  • You can specify the DPI when you save a plot to a file:
using matplotlib.pyplot as mplot
mplot.savefig("foo.png", dpi=300)
  • If you want this DPI to be used for all output by Matplotlib, then set this line in your matplotlibrc file:
savefig.dpi: 300

Tried with: Matplotlib 1.4.3, Python 2.7.6 and Ubuntu 14.04

How to remove padding around plot in Matplotlib

The plot generated by Matplotlib typically has a lot of padding around it. This is useful if you are viewing or displaying the plot in isolation. However, when the plot is embedded inside another document, typically extra padding is added around and makes the plot look tiny. The solution is to reduce or remove the padding around the plot generated by Matplotlib.

This can be done by configuring the bounding box used for the plot while saving it to disk:

import matplotlib.pyplot as mplot
mplot.savefig("foo.pdf", bbox_inches="tight")

This makes the bounding box tight around the plot, while still giving enough space for the text or lines on the plot periphery.

If you want a plot with zero padding around it:

import matplotlib.pyplot as mplot
mplot.savefig("foo.pdf", bbox_inches="tight", pad_inches=0)

Personally, I find this too tight, but it might be useful in some situations.

Tried with: Matplotlib 1.4.3, Python 2.7.3 and Ubuntu 14.04