How do you remove the documentation installed by gem install? - ruby

I know it's possible to install a gem without the documentation, but unfortunately, I didn't for the first three months I used ruby. In that time, I managed to install a significant amount of gems, but not once since I started using ruby have I used the documentation on my computer. I always look to docs on the internet.
What is the best way to safely remove the documentation from my computer? Also, is there a way to configure ruby to not install documentation by default?

run this command:
rm -r "$(gem env gemdir)"/doc/*
on windows if you use cygwin

Gem documentation is installed in [RUBY]/lib/ruby/gems/doc, where [RUBY] is the directory into which you installed Ruby (C:/ruby on my machine).
I don't see why you shouldn't just delete the folders representing the gems for which don't don't need documentation: you can always regenerate it.

I understand there is a .gemrc file that may be used.
install: --no-rdoc --no-ri
update: --no-rdoc --no-ri
puts the two lines in it.
I believe you put the .gemrc file in your $Home directory.

Copy & paste any of these into a Bourne-compatible shell:
Remove existing docs for locally-installed gems, current-user only
(exec 1>&2; DIR="$(gem env gemdir)"; \
DOCDIR="$DIR"/doc; \
if [ -d "$DOCDIR" ]; then \
echo "Contents of '$DOCDIR': "; ls "$DOCDIR"/; echo ''; \
read -p "Do you really want to remove contents of RubyGems doc dir '$DOCDIR' ? [Yn] " ANS; \
if [ "$ANS" = y ] || [ "$ANS" = Y ]; then \
rm -rf "$DOCDIR"/*; \
fi; \
fi)
Remove docs for all globally installed system gems
(exec 1>&2; for DIR in $(gem env path | tr ':' '\n'); do \
DOCDIR="$DIR"/doc; \
if [ -d "$DOCDIR" ]; then \
echo "Contents of '$DOCDIR': "; ls "$DOCDIR"/; echo ''; \
echo '!! Possibly removes system-provided gem docs, review carefully before continuing ("n" if unsure or Ctrl-C to abort completely) !!'; \
read -p "Do you really want to remove RubyGems doc dir '$DOCDIR' ? [Yn] " ANS; \
if [ "$ANS" = y ] || [ "$ANS" = Y ]; then \
sudo rm -rf "$DOCDIR"/*; \
fi; \
fi; \
done)
Prevent current user from generating gems docs from now onwards
with modern RubyGems:
(X='gem: --no-document'; \
touch ~/.gemrc && \
grep -q "^$X$" ~/.gemrc || echo "$X" >> ~/.gemrc)
with old RubyGems: X='gem: --no-ri --no-rdoc' ...

I can't answer the first part of your problem (I have the same issue myself) but I have managed to not install documentation by default.
You can specify some default command line options in the gem config file, you can specify not to generate documentation (something like --no-rdoc and --no-ri).
Sam

This command worked for me in removing documentation installed by install.
rm -r "$(gem env gemdir)"/doc/*
Here's another way to go about it;
To locate the folder where gems are installed on your system, simply run the following on terminal or shell
gem env home
This will display an output like
/home/username/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0
You can then locate the docs folder and delete all the documentations that you find there for each gem in a ruby version
I also want to add this too to the answers provided,
if want to prevent gems from generating documentation during installation, then turn off local documentation generation by creating a file called ~/.gemrc which contains a configuration setting to turn off this feature:
Simply run this code in your terminal or shell
printf "install: --no-rdoc --no-ri\nupdate: --no-rdoc --no-ri\n" >> ~/.gemrc
OR
echo "gem: --no-document" > ~/.gemrc
This will prevent gems from generating documentation during installation, saving you the stress of deleting/removing gem documentations on your system later.
That's all
I hope this helps.

Related

Unable to install Ruby on Ubuntu

I'm trying to install Ruby 2.6.1 on Ubuntu but keep coming across this error.
Have tried uninstalling Ubuntu, googling the problem, running in administrator mode, downloading a different version.
This is the error:
:~$ rvm install 2.6.1
Searching for binary rubies, this might take some time.
Found remote file https://rvm_io.global.ssl.fastly.net/binaries/ubuntu/20.04/x86_64/ruby-2.6.1.tar.bz2
Checking requirements for ubuntu.
Installing requirements for ubuntu.
mkdir: cannot create directory ‘/usr/share/rvm/log/1625135668_ruby-2.6.1’: Permission denied
tee: /usr/share/rvm/log/1625135668_ruby-2.6.1/update_system.log: No such file or directory
Updating system..jaydene password required for 'apt-get --quiet --yes update':
Sorry, try again.
jaydene password required for 'apt-get --quiet --yes update':
..
Error running 'requirements_debian_update_system ruby-2.6.1',
please read /usr/share/rvm/log/1625135668_ruby-2.6.1/update_system.log
Requirements installation failed with status: 1.
:~$
you can use a gorails website to checkout ruby on rails installation. And ruby installs version as per dependencies on your system. If it's not founding 2.6.1 it will install next supported version for ex. 2.6.7.
check this website: https://gorails.com/setup/ubuntu/21.04
Check this: How do I install Ruby 1.9.3 on Ubuntu without RVM? but SO says to avoid link answers so here is the script:
#!/usr/bin/env bash
# -- this really is the only solution that worked for me on snap :/
ruby -v
if ! command -v ruby &> /dev/null
then
echo "Going to try to install ruby (ideally 3.1.2)"
# - install rebenv (following ruby-build really is needed eventhough it doesn't look like it)
mkdir -p ~/.rbenv
cd ~/.rbenv
git clone https://github.com/rbenv/rbenv.git .
# if $HOME/.rbenv/bin not in path append it, otherwise don't change it
echo $PATH | tr ':' '\n' | awk '{print " " $0}';
if [[ ":$PATH:" != *":$HOME/.rbenv/bin:"* ]]; then
echo "might want to put $HOME/.rbenv/bin in your path"
export PATH="$HOME/.rbenv/bin:$PATH"
# echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc.lfs
fi
eval "$(rbenv init -)"
rbenv -v
# - install ruby-build, odd, this really is needed for ruby to install despite it not looking like ruby build is need at the bottom
mkdir -p ~/.ruby-build
cd ~/.ruby-build
git clone https://github.com/rbenv/ruby-build.git .
# if $HOME/.ruby-build/bin not in path append it, otherwise don't change it
echo $PATH | tr ':' '\n' | awk '{print " " $0}';
if [[ $PATH != *"$HOME/.ruby-build/bin"* ]]; then
echo "might want to put $HOME/.ruby-build/bin in your path"
export PATH="$HOME/.ruby-build/bin:$PATH"
# echo 'export PATH="$HOME/.ruby-build/bin:$PATH"' >> ~/.bashrc.lfs
fi
ruby-build --version
# - install ruby without sudo -- using rbenv
mkdir -p ~/.local
# ruby-build 3.1.2 ~/.local/
rbenv install 3.1.2
rbenv global 3.1.2
fi
ruby -v
# - Original Prover doesn't work on SNAP
# Proverbot's way to install ruby
# # First, install Ruby, as that is for some reason required to build the "system" project
# git clone https://github.com/rbenv/ruby-build.git ~/ruby-build
# mkdir -p ~/.local
# PREFIX=~/.local ./ruby-build/install.sh
# ~/.local/ruby-build 3.1.2 ~/.local/
# ref: https://superuser.com/questions/340490/how-to-install-and-use-different-versions-of-ruby/1756372#1756372

How can I make dpkg automatically choose my own virtual package over an already installed one?

I have several packages that provide the same functionality - and in the device
'there can be only one' at the same time.
I have read about the 'Provides, Conflicts, Replaces' in the debian policy, but I have not found a way (using dpkg with commands/switches) to automatically replace an already installed virtual package without removing it manually first.
My package's control file specifies the following for all the packages in question:
Provides: myown-virtual-package
Conflicts: myown-virtual-package
Replaces: myown-virtual-package
Here is what I do, It seems to work, but I was wondering if there is a standard way using only dpkg
# remove any conflicting virtual packages
for i in /tmp/upgrade_software/*.deb
do
# find out what package name and what it provides
provides_line=$(dpkg --info $i | grep "^ Provides: ")
package_line=$(dpkg --info $i | grep "^ Package: ")
virt_package=${provides_line##*: }
this_package=${package_line##*: }
# skip if it is not a virtual package
[ -z "${virt_package}" ] && continue
# remove any package that provides the same
otherpackage_line=$(dpkg-query -W -f='${Provides}: ${Package}\n' \
| grep "${virt_package}:" | grep -v ${this_package})
if [ -n "${otherpackage_line}" ] ; then
otherpackage=${otherpackage_line##*: }
echo " ------ removing ${otherpackage} because of conflict -------"
dpkg --purge ${otherpackage}
echo " -------------"
fi
echo \'$virt_package\' checked for conflicts
done
Thanks in advance, jj
dpkg will not take this kind of automatic conflict resolution measures. For these tasks, there is apt-get and aptitude. It may just work with
dpkg -i package.deb ; apt-get -f install
The latter command is supposed to resolve the conflicts. If it opts to remove your own package for resolution, you may even want to try
dpkg -i package.deb ; apt-get -f install <package>
I.e., tell apt to install your package (without a .deb extension) as it should now be visible to apt.
This can be done with dpkg alone, by giving it enough information so that it can perform the operation. The way to prepare dpkg for this is via selections.
In this case you'd tell that removing the old provider is ok, and then when you install the new one dpkg will be able to remove the other package during the upgrade.
Try something like:
echo old-provider deinstall | dpkg --set-selections
dpkg -iB new-provider.deb
That should in principle do it, and no need for apt-get fixing it up (-f), or for pre purges (possibly with --force options if there are packages depending on the virtuals).

Script for Installing missing packages via dpkg-query

I've got a script working to echo out the packages missing in the command line but would like a option to add in control for install the missing applications. What would the best way go to doing this? apt-get install $tmp?
#!/bin/bash
echo "Checking server for missing packages..."
tmp=$(dpkg-query -W -f='${Package} ${Status} \n' apache2 openssh-server php5 \
php5-cli php5-xsl php5-imap php5-curl | grep "no package found")
if [[ $tmp =~ "no package found" ]]
then
echo "working"
apt-get install $tmp
else
echo "foo"
fi
I've tried to add a loop into it but not getting the output from $tmp
Thanks
apt-get won't complain if you name packages already installed on your system.
Given this fact, I wouldn't bother trying to filter out already installed packages and just use the following one-liner.
apt-get install apache2 openssh-server php5 php5-cli php5-xsl php5-imap php5-curl
You might want to add -qy switches to run quietly and without beeing asked any question.

What's wrong with my bash array?

Can anyone tell me why this bash script works if I cut and paste it to the terminal but throws "server_prep.sh: 7: Syntax error: "(" unexpected" when launched using $ sudo sh server_prep.sh ?
#!/bin/sh
#Packages
apt-get -y install ssh libsqlite3-dev ruby-full mercurial
#Gems
required_gems = ( rake rails sqlite3-ruby )
#Set up directories
[ ! -d /var/www ] && mkdir /var/www
[ ! -d /var/www/apps ] && mkdir /var/www/apps
#install gems manually
if ! which gem >/dev/null; then
wget http://rubyforge.org/frs/download.php/60718/rubygems-1.3.5.tgz
tar xvfz rubygems-1.3.5.tgz
ruby rubygems-1.3.5/setup.rb
ln -s /usr/bin/gem1.8 /usr/bin/gem
gem update --system
#Tidy Up
rm -rf rubygems-1.3.5.tgz rubygems-1.3.5
fi
#Install required gems
for required_gem in "${required_gems[#]}"
do
if ! gem list | grep $required_gem >/dev/null; then
gem install $required_gems
fi
done
Thanks in advance!
Are you on ubuntu?
Then you should change the #!-line at the top to read #!/bin/bash because /bin/sh is a very limited shell.
This would explain why works in the terminal (where the shell is bash) but not as a shell script (which is run by /bin/sh).
They changed this a couple of releases ago for performance reasons - most people don't need full bash functionality for shell script, and this limited shell is much faster at startup.
Edit: I just noticed that you don't even have to use an array since you convert it to a space separated string in the for loop anyway. Just remove the parenthesis in the assignment and put quotes around it instead (and also remove the spaces around the equal sign, as hacker suggested)
Try
required_gems=( rake rails sqlite3-ruby )
instead (note the lack of spaces around '=').

How can I check if a package is installed and install it if not?

I'm working on a Ubuntu system and currently this is what I'm doing:
if ! which command > /dev/null; then
echo -e "Command not found! Install? (y/n) \c"
read
if "$REPLY" = "y"; then
sudo apt-get install command
fi
fi
Is this what most people would do? Or is there a more elegant solution?
To check if packagename was installed, type:
dpkg -s <packagename>
You can also use dpkg-query that has a neater output for your purpose, and accepts wild cards, too.
dpkg-query -l <packagename>
To find what package owns the command, try:
dpkg -S `which <command>`
For further details, see article Find out if package is installed in Linux and dpkg cheat sheet.
To be a little more explicit, here's a bit of Bash script that checks for a package and installs it if required. Of course, you can do other things upon finding that the package is missing, such as simply exiting with an error code.
REQUIRED_PKG="some-package"
PKG_OK=$(dpkg-query -W --showformat='${Status}\n' $REQUIRED_PKG|grep "install ok installed")
echo Checking for $REQUIRED_PKG: $PKG_OK
if [ "" = "$PKG_OK" ]; then
echo "No $REQUIRED_PKG. Setting up $REQUIRED_PKG."
sudo apt-get --yes install $REQUIRED_PKG
fi
If the script runs within a GUI (e.g., it is a Nautilus script), you'll probably want to replace the 'sudo' invocation with a 'gksudo' one.
This one-liner returns 1 (installed) or 0 (not installed) for the 'nano' package...
$(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed")
even if the package does not exist or is not available.
The example below installs the 'nano' package if it is not installed...
if [ $(dpkg-query -W -f='${Status}' nano 2>/dev/null | grep -c "ok installed") -eq 0 ];
then
apt-get install nano;
fi
dpkg-query --showformat='${db:Status-Status}'
This produces a small output string which is unlikely to change and is easy to compare deterministically without grep:
pkg=hello
status="$(dpkg-query -W --showformat='${db:Status-Status}' "$pkg" 2>&1)"
if [ ! $? = 0 ] || [ ! "$status" = installed ]; then
sudo apt install $pkg
fi
The $? = 0 check is needed because if you've never installed a package before, and after you remove certain packages such as hello, dpkg-query exits with status 1 and outputs to stderr:
dpkg-query: no packages found matching hello
instead of outputting not-installed. The 2>&1 captures that error message too when it comes preventing it from going to the terminal.
For multiple packages:
pkgs='hello certbot'
install=false
for pkg in $pkgs; do
status="$(dpkg-query -W --showformat='${db:Status-Status}' "$pkg" 2>&1)"
if [ ! $? = 0 ] || [ ! "$status" = installed ]; then
install=true
break
fi
done
if "$install"; then
sudo apt install $pkgs
fi
The possible statuses are documented in man dpkg-query as:
n = Not-installed
c = Config-files
H = Half-installed
U = Unpacked
F = Half-configured
W = Triggers-awaiting
t = Triggers-pending
i = Installed
The single letter versions are obtainable with db:Status-Abbrev, but they come together with the action and error status, so you get 3 characters and would need to cut it.
So I think it is reliable enough to rely on the uncapitalized statuses (Config-files vs config-files) not changing instead.
dpkg -s exit status
This unfortunately doesn't do what most users want:
pkgs='qemu-user pandoc'
if ! dpkg -s $pkgs >/dev/null 2>&1; then
sudo apt-get install $pkgs
fi
because for some packages, e.g. certbot, doing:
sudo apt install certbot
sudo apt remove certbot
leaves certbot in state config-files, which means that config files were left in the machine. And in that state, dpkg -s still returns 0, because the package metadata is still kept around so that those config files can be handled more nicely.
To actually make dpkg -s return 1 as desired, --purge would be needed:
sudo apt remove --purge certbot
which actually moves it into not-installed/dpkg-query: no packages found matching.
Note that only certain packages leave config files behind. A simpler package like hello goes directly from installed to not-installed without --purge.
Tested on Ubuntu 20.10.
Python apt package
There is a pre-installed Python 3 package called apt in Ubuntu 18.04 which exposes an Python apt interface!
A script that checks if a package is installed and installs it if not can be seen at: How to install a package using the python-apt API
Here is a copy for reference:
#!/usr/bin/env python
# aptinstall.py
import apt
import sys
pkg_name = "libjs-yui-doc"
cache = apt.cache.Cache()
cache.update()
cache.open()
pkg = cache[pkg_name]
if pkg.is_installed:
print "{pkg_name} already installed".format(pkg_name=pkg_name)
else:
pkg.mark_install()
try:
cache.commit()
except Exception, arg:
print >> sys.stderr, "Sorry, package installation failed [{err}]".format(err=str(arg))
Check if an executable is in PATH instead
See: How can I check if a program exists from a Bash script?
See also
https://askubuntu.com/questions/165951/dpkg-get-selections-shows-packages-marked-deinstall
https://askubuntu.com/questions/423355/how-do-i-check-if-a-package-is-installed-on-my-server
Ubuntu added its "Personal Package Archive" (PPA), and PPA packages have a different result.
A native Debian repository package is not installed:
~$ dpkg-query -l apache-perl
~$ echo $?
1
A PPA package registered on the host and installed:
~$ dpkg-query -l libreoffice
~$ echo $?
0
A PPA package registered on the host, but not installed:
~$ dpkg-query -l domy-ce
~$ echo $?
0
~$ sudo apt-get remove domy-ce
[sudo] password for user:
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package domy-ce is not installed, so not removed
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Also posted on: Test if a package is installed in APT
UpAndAdam wrote:
However you can't simply rely on return codes here for scripting
In my experience you can rely on dkpg's exit codes.
The return code of dpkg -s is 0 if the package is installed and 1 if it's not, so the simplest solution I found was:
dpkg -s <pkg-name> 2>/dev/null >/dev/null || sudo apt-get -y install <pkg-name>
It works fine for me...
I've settled on one based on Nultyi's answer:
MISSING=$(dpkg --get-selections $PACKAGES 2>&1 | grep -v 'install$' | awk '{ print $6 }')
# Optional check here to skip bothering with apt-get if $MISSING is empty
sudo apt-get install $MISSING
Basically, the error message from dpkg --get-selections is far easier to parse than most of the others, because it doesn't include statuses like "deinstall". It also can check multiple packages simultaneously, something you can't do with just error codes.
Explanation/example:
$ dpkg --get-selections python3-venv python3-dev screen build-essential jq
dpkg: no packages found matching python3-venv
dpkg: no packages found matching python3-dev
screen install
build-essential install
dpkg: no packages found matching jq
So grep removes installed packages from the list, and awk pulls the package names out from the error message, resulting in MISSING='python3-venv python3-dev jq', which can be trivially inserted into an install command.
I'm not blindly issuing an apt-get install $PACKAGES, because as mentioned in the comments, this can unexpectedly upgrade packages you weren't planning on; not really a good idea for automated processes that are expected to be stable.
This seems to work pretty well.
$ sudo dpkg-query -l | grep <some_package_name> | wc -l
It either returns 0 if not installed or some number > 0 if installed.
It seems that nowadays apt-get has an option --no-upgrade that just does what the OP wants:
--no-upgrade Do not upgrade packages. When used in conjunction with install, no-upgrade will prevent packages listed from being upgraded if they are already installed.
Manpage from https://linux.die.net/man/8/apt-get
Therefore you can use
apt-get install --no-upgrade package
and package will be installed only if it's not.
Inspired by Chris' answer:
#! /bin/bash
installed() {
return $(dpkg-query -W -f '${Status}\n' "${1}" 2>&1|awk '/ok installed/{print 0;exit}{print 1}')
}
pkgs=(libgl1-mesa-dev xorg-dev vulkan-tools libvulkan-dev vulkan-validationlayers-dev spirv-tools)
missing_pkgs=""
for pkg in ${pkgs[#]}; do
if ! $(installed $pkg) ; then
missing_pkgs+=" $pkg"
fi
done
if [ ! -z "$missing_pkgs" ]; then
cmd="sudo apt install -y $missing_pkgs"
echo $cmd
fi
This will do it. apt-get install is idempotent.
sudo apt-get install --no-upgrade command
I've found all solutions in previous answers can produce a false positive if a package is installed and then removed, yet the installation package remains on the system.
To replicate:
Install package apt-get install curl
Remove package apt-get remove curl
Now test the previous answers.
The following command seems to solve this condition:
dpkg-query -W -f='${Status}\n' curl | head -n1 | awk '{print $3;}' | grep -q '^installed$'
This will result in a definitive installed or not-installed.
$name="rsync"
[ `which $name` ] $$ echo "$name : installed" || sudo apt-get install -y $name
Use:
apt-cache policy <package_name>
If it is not installed, it will show:
Installed: none
Otherwise it will show:
Installed: version
This feature already exists in Ubuntu and Debian, in the command-not-found package.
This explicitly prints 0 if installed else 1 using only awk:
dpkg-query -W -f '${Status}\n' 'PKG' 2>&1|awk '/ok installed/{print 0;exit}{print 1}'
or if you prefer the other way around where 1 means installed and 0 otherwise:
dpkg-query -W -f '${Status}\n' 'PKG' 2>&1|awk '/ok installed/{print 1;exit}{print 0}'
** replace PKG with your package name
Convenience function:
installed() {
return $(dpkg-query -W -f '${Status}\n' "${1}" 2>&1|awk '/ok installed/{print 0;exit}{print 1}')
}
# usage:
installed gcc && echo Yes || echo No
#or
if installed gcc; then
echo yes
else
echo no
fi
For Ubuntu, apt provides a fairly decent way to do this. Below is an example for Google Chrome:
apt -qq list google-chrome-stable 2>/dev/null | grep -qE "(installed|upgradeable)" || apt-get install google-chrome-stable
I'm redirecting error output to null, because apt warns against using its "unstable cli". I suspect list package is stable, so I think it's ok to throw this warning away. The -qq makes apt super quiet.
which <command>
if [ $? == 1 ]; then
<pkg-manager> -y install <command>
fi
This command is the most memorable:
dpkg --get-selections <package-name>
If it's installed it prints:
<package-name> install
Otherwise it prints
No packages found matching <package-name>.
This was tested on Ubuntu 12.04.1 (Precise Pangolin).
apt list [packagename]
seems to be the simplest way to do it outside of dpkg and older apt-* tools.
I had a similar requirement when running test locally instead of in Docker. Basically I only wanted to install any .deb files found if they weren't already installed.
# If there are .deb files in the folder, then install them
if [ `ls -1 *.deb 2> /dev/null | wc -l` -gt 0 ]; then
for file in *.deb; do
# Only install if not already installed (non-zero exit code)
dpkg -I ${file} | grep Package: | sed -r 's/ Package:\s+(.*)/\1/g' | xargs dpkg -s
if [ $? != 0 ]; then
dpkg -i ${file}
fi;
done;
else
err "No .deb files found in '$PWD'"
fi
I guess the only problem I can see is that it doesn't check the version number of the package so if .deb file is a newer version. Then this wouldn't overwrite the currently installed package.
Kinda based off yours just a little more 'elegant'. Just because I'm bored.
#!/bin/bash
FOUND=("\033[38;5;10m")
NOTFOUND=("\033[38;5;9m")
PKG="${#:1}"
command ${PKG} &>/dev/null
if [[ $? != 0 ]]; then
echo -e "${NOTFOUND}[!] ${PKG} not found [!]"
echo -e "${NOTFOUND}[!] Would you like to install ${PKG} now ? [!]"
read -p "[Y/N] >$ " ANSWER
if [[ ${ANSWER} == [yY] || ${ANSWER} == [yY][eE][sS] ]]; then
if grep -q "bian" /etc/os-release; then
sudo apt-get install ${PKG}
elif grep -q "arch" /etc/os-release; then
if [[ -f /bin/yay ]] || [[ -f /bin/yaourt ]]; then
yaourt -S ${PKG} 2>./err || yay -S ${PKG} 2>./err
else
sudo pacman -S ${PKG}
fi
elif grep -q "fedora" /etc/os-release; then
sudo dnf install ${PKG}
else
echo -e "${NOTFOUND}[!] This script couldn't detect your package manager [!]"
echo -e "${NOTFOUND}[!] Manually install it [!]"
fi
elif [[ ${ANSWER} == [nN] || ${ANSWER} == [nN][oO] ]]; then
echo -e "${NOTFOUND}[!] Exiting [!]"
fi
else
echo -e "${FOUND}[+] ${PKG} found [+]"
fi
The answers that suggest to use something along the lines of:
dpkg-query --showformat '${db:Status-Status}\n' --show $package | grep -q '^installed$'
dpkg-query --showformat '${Status}\n' --show $package | grep -q '^install ok installed$'
are correct.
But if you have the package dpkg-dev installed and you do not just want to check whether a package is installed but you also:
want to know whether a package is installed in a certain version
want to have a package in a certain architecture
want to see if a virtual package is provided
then you can abuse the dpkg-checkbuilddeps tool for this job:
dpkg-checkbuilddeps -d apt /dev/null
This will check whether apt is installed.
The following will check whether apt is installed in at least version 2.3.15 and grep is installed as amd64 and the virtual package x-window-manager is provided by some of the installed packages:
dpkg-checkbuilddeps -d 'apt (>= 2.3.15), grep:amd64, x-window-manager' /dev/null
The exit status of dpkg-checkbuilddeps will tell the script whether the dependencies are satisfied or not. Since this method supports passing multiple packages, you only have to run dpkg-checkbuilddeps once even if you want to check whether more than one package is installed.
Since you mentioned Ubuntu, and you want to do this programmatically(although dpkg variations can also be used but would be more complex to implement), this(which) will definitely work:
#!/bin/bash
pkgname=mutt
which $pkgname > /dev/null;isPackage=$?
if [ $isPackage != 0 ];then
echo "$pkgname not installed"
sleep 1
read -r -p "${1:-$pkgname will be installed. Are you sure? [y/N]} " response
case "$response" in
[yY][eE][sS]|[yY])
sudo apt-get install $pkgname
;;
*)
false
;;
esac
else
echo "$pkgname is installed"
sleep 1
fi
Although for POSIX compatibility, you would want to use command -v instead as mentioned in another similar question.
In that case,
which $pkgname > /dev/null should be replaced by command -v $pkgname in the above code sample.
If your package has a command line interface you can check if the package exists before installing it by evaluating the output from calling it's command line tool.
Here's an example with a package called helm.
#!/bin/bash
# Call the command for the package silently
helm > /dev/null
# Get the exit code of the last command
command_exit_code="$(echo $?)"
# Run installation if exit code is not equal to 0
if [ "$command_exit_code" -ne "0" ]; then
# Package does not exist: Do the package installation
else
echo "Skipping 'helm' installation: Package already exists"
fi;
I use the following way:
which mySQL 2>&1|tee 1> /dev/null
if [[ "$?" == 0 ]]; then
echo -e "\e[42m MySQL already installed. Moving on...\e[0m"
else
sudo apt-get install -y mysql-server
if [[ "$?" == 0 ]]; then
echo -e "\e[42mMy SQL installed\e[0m"
else
echo -e "\e[42Installation failed\e[0m"
fi
fi
I use this solution as I find it most straightforward.
function must_install(){
return "$(apt -qq list $var --installed 2> /dev/null |wc -l)"
}
function install_if() {
unset install
for var in "$#"
do
if $(must_install $var)
then
install+="${var} "
fi
done
if [ -n "$install" ];
then
sudo apt-get install -qy $install
fi
}
The neat thing is, must_install returns 1 or 0 which is then interpreted as true or false from the calling if, so we don't need any test using [].
install_if takes any number of packages separated by space.
The problem is apt is not meant to be used in scripts, so this might stop working at any time. 8)
All the answers are good but seem to be complex for beginners like myself to understand. so here is the solution that worked for me. My Linux environment is centOS but can't confirm it works for all distributions
PACKAGE_NAME=${PACKAGE_NAME:-node}
if ! command -v $PACKAGE_NAME > /dev/null; then
echo "Installing $PACKAGE_NAME ..."
else
echo "$PACKAGE_NAME already installed"
fi

Resources