Thursday, March 5, 2020

Swapping CapsLock to Esc key in openSUSE 15.+

Being a vim user I have adopted my keyboard to swap the caps key from the esc key. In openSUSE the file /etc/vconsole.conf needs to be edited in order to make the changes permanent.

1.  First thing we need to do is create a new directory somewhere in /usr

  mkdir -p /usr/local/share/kbd/keymaps/ 

2.  Next change directory to the newly created directory.

  cd /usr/local/share/kbd/keymaps/  

3.  Next copy the keyboard layout/setup that you're using from /usr/share/kbd/keymaps/xkbd.

  cp -v /usr/share/kbd/keymaps/xkb/ /usr/local/share/kbd/keymaps/  

5.   ls 

You should see the output.  

6. unzip it.


7. Check the default value of the keycodes for Escape and Caps_Lock.

  grep -noE '^keycode[[:space:]]*(1|58)[[:space:]]*=[[:space:]][^ ]*'  

   The output should be like this.

  2:keycode 1 = Escape  
  59:keycode 58 = CtrlL_Lock 

8. Change value of key code 1 to 58 and 58 to 1.

printf '%s\n' '/^\(keycode[[:space:]]*\)1\([[:space:]]*=.*$\)/s//\158\2/' \
'/^\(keycode[[:space:]]*\)58\([[:space:]]*=.*$\)/s//\11\2/' ,p Q | ed -s \ 
| grep -noE '^keycode[[:space:]]*(1|58)[[:space:]]*=[[:space:]][^ ]*'

Should print the output below, since that ed code does not edit the file in-place but just prints the output to stdout.

  2:keycode 58 = Escape  
  59:keycode 1 = CtrlL_Lock  

To actually edit the file you have two options using ed.

Using printf with ed.

 printf '%s\n' '/^\(keycode[[:space:]]*\)1\([[:space:]]*=.*$\)/s//\158\2/' \
'/^\(keycode[[:space:]]*\)58\([[:space:]]*=.*$\)/s//\11\2/' w | ed -s

Using a  heredoc with ed.

ed -s << 'EOF'  

9. Check the new value the keycodes Escape and Caps_Lock

   grep -noE '^keycode[[:space:]]*(1|58)[[:space:]]*=[[:space:]][^ ]*'  

 The output should be like this.

  2:keycode 58 = Escape  
  59:keycode 1 = CtrlL_Lock 

10. Save it to /usr/local/share/kbd/keymaps/

  mv -v  

11. To load during the session

  loadkeys /usr/local/share/kbd/keymaps/ 

12. To make permanent In order to load the keymap at boot, specify the full path to the file in the KEYMAP variable in /etc/vconsole.conf. The file does not have to be gzipped as the official keymaps provided by kbd.

   Check the value of /etc/vconsole.conf

  cat /etc/vconsole.conf 

You should see what is the default setup in that config file.

13 .Back up first vconsole.conf before editing that file.

  cp -v /etc/vconsole.conf /etc/vconsole.conf.original  

14. Replace the value of KEYMAP to the absolute path of the new key mappings.

  printf '%s\n' '/^\(KEYMAP\).*/s//\1=\/usr\/local\/share\/kbd\/keymaps\/' w | ed -s /etc/vconsole.conf  

15. Check the value of the newly edited /etc/vconsole.conf file.

  cat /etc/vconsole.conf  

Sunday, July 22, 2018

Keeping the Bash shell history

There are a lot howto's and instructions about keeping the shell's history but my favorite is my own fork of which is sdb. Now you're probably asking as to why would anyone wants to keep the history of all the commands you have written during the interactive shell? Well for starters you can check which commands did you executed during the last week on which directory or the exit status of that command. or you want to check for the commands that has a word  foo  somewhere in there or you remember a very cool command that has a lot of combinations of pipes, subshell, redirections and so on, that you have had executed but forgot the exact syntax.

 The history is kept in a file called .bash_history inside your $HOME directory. A normal text file has its limitations because of the file size. Imagine even if you manage to save all the history in one file then that file could get bigger and bigger depending on how much data in contains.

In openSUSE Leap 15.0 syslog can be used to log bash history according to this
site which actually works as expected/advertised, unfortunately sdb can't be used 
in parallel with that set up. Just don't edit the /etc/bash.bashrc file instead create a
new file named /etc/bash.bashrc.local .

Any how sdb saves the history in a sqlite3 database, why sqlite? because it has no server client authentication. If you have read and write to the database file then you can use that database there is no admin privilege involve. Quoting what google has to say about sqlite

Unlike other databases (like SQL Server and MySQL) SQLite does not support stored procedures. SQLite is file-based, unlike other databases, like SQL Server and MySQL which are server-based. SQL is a Structured Query Language used to query a Database usually Relational Database Systems.

Sqlite is being used by some of the popular web browsers like firefox and most modern smart phone also like Android. Yes there is always a risk of sql-injection  which can either collect user info or destroy the database and worsts it can blocked everybody in the entire system but since sqlite is only a file based then the risk is minimal. No authentication on the server, client side needed. The worst that could happen is you lose your history table from the database. At the point the "Always keep a backup" comes in.

For those of you who does not know how to use vim  you should at least know howto exit or quit vim without editing a file. Since vim is the default  value of the environment variable EDITOR if it is empty. Press the escape key and then key in the colon and the q key followed by an exclamation key.


Create a cron job to automatically save the database at some safe place. An example on howto do it in openSUSE every hour.

   0 * * * * /usr/bin/sqlite3 database ".backup /path/to/new/safe/location/database"   

Give the absolute path of the sqlite3 executable and replace the database with the correct name and path and the path to the new database  file. Note that the database will be over ridden every hour so it is wise to create a script and just use cron to execute it

Now lets see how we can use sdb in the interactive shell.

Let's say you want to look for commands in the current session e.g. currently open terminal
that you're using. Type


If you just open a new terminal then the only thing you will see are the headers. Which
looks like this (with the -c option).

 |     ID         |  COUNT |    DATE/TIME   |    USER@HOST   | STATUS   |    TTY     | DIRECTORY |▶ COMMAND |

Now if you want to see the other shell sessions and current ones. e.g. if there are other terminals that are open and you or others that is using sdb. Use the -a option, which defaults to 100 lines only.

   sdb -a   

If you want to see all the commands starting from the very beginning add the -m option and use the + sign to specify the maximum value instead of typing 999999999999999999 for the value of -m. Yes they are 18 9's!

  sdb -am+  

Change the + sign into some number/digit to specify how much lines you want.

If you want to see the directory, exit status, user and host, then add the -c option.

   sdb -cam+  

The -u, -h, -l and -d can be specified for specific query as well.

If you want to see sqlite commands that has been executed then add the -v option.

   sdb -vcam+   

To check for commands that starts with zypper use the -b option

   sdb -vcam+ -b zypper   

To check the commands that starts with zypper and ends with bash add the -e option.

   sdb -vcam+ -b zypper -e bash  

On the other hand the -w option can do that also

    sdb -vcam+ -w 'zypper%bash'   

The % is like the glob in the shell which is * , but the % is for sqlite.

To check for commands that was executed from 10 days ago and now.

   sdb -cam+ -t '-10 days, now'   

To check the commands that was executed in the directory /foo and all the directory under it
recursive search.

    sdb -vcam+ -r /foo   

It is also possible to re execute the commands that you can find in the query by providing
the ID of the command. Run command with the id 100

  sdb -E 100  

Now some example of long combination of options.

    sdb -am+ -n0 -w 'zypper%bash' -u jetchisel -h leap150 -r "$HOME" -t '-1 year, -2 days'    

The -a for all commands from the other sessions and current ones. (100 lines only.)
The -m+ for all the commands from the beginning.
 First the  -n option with the zero argument checks for the commands that exited with a zero status.
The -w for commands that starts with zypper and ends with bash.
The -u for the username of the user
The -h for the hostname  of the computer.
The -r for a recursive search for commands on and under the directory "$HOME"
The -t is for searching commands starting from last year until 2 days ago.

Enjoy folks....

Friday, June 3, 2016

How to Install packages from my hard drive.

Most if not all packages (files ending in *.rpm) are on the online repositories. In some cases you have the rpm from your pc or you have downloaded it from some site/server etc.  Question is how would one install it?

Simple answer is to use the rpm utility since openSUSE is one of the rpm-based distro like Rhel and Centos but we will not use that in our example since openSUSE has zypper.

To install the package name foo

    zypper in foo.rpm

or one can use the absolute path

    zypper in /path/to/foo.rpm


    zypper in /var/run/media/jetchisel/foo.rpm 

Another question is: 

What if you have a bunch of packages (rpm's) that you want to install?  

One can work around it by using some shell tricks on the cli. One in particular is the use of glob and an array.

Save the rpm's in an array.


or use an absolute path.




Install the rpm's

    zypper in "${packages[@]}"

The last but not the least question.  

I have a bunch of rpm's which are the dependencies from the program/package that is located on the online repositories.

The alternative question is:    

how can I make a local directory (folder) to become a local repository?

First option is using zypper.

   zypper ar -t plaindir . local 

or one can use an absolute path for the directory.

   zypper ar -t plaindir  /path/to/rpm local 

let's take out that zypper code piece by piece.

 zypper ar  is add repository

  -t plaindir   means type is a plain directory (folder)

the    .    (dot)  means where you are located the value of the command    pwd  
  /path/to/rpm      is the absolute path.

   local   is the alias of the repository and it is arbitrary

Once the directory (folder) is added as a repo you can now refresh it.

   zypper ref local

Print the packages from the local repo.

   zypper --no-refresh se -t package -r local

Now install the package(s) and enjoy! :)

One can look for more option using the help option for zypper.

   zypper help ar 

The second option is using yast2 in the following order.

  1. yast2 
  2. Software Repositories
  3. Add
  4. Local Directory

Thursday, June 2, 2016

How to revert packages from the previous repo

 The most common usecase doing a vendor change of packages

You did a dup to a specific repository but you want to revert back the packages to the previous repository.

First find out the list of your repos.

  •   zypper lr  

#  | Alias                                           | Name                                                               | Enabled | Refresh
 1 | Packman                                   | Packman                                                           | Yes     | No    
 2 | libdvdcss                                   | libdvdcss                                                            | Yes     | No    
 3 | openSUSE-13.2-0                    | openSUSE-13.2-0                                            | Yes     | No    
 4 | repo-debug                               | openSUSE-13.2-Debug                                   | No       | No    
 5 | repo-debug-update                 | openSUSE-13.2-Update-Debug                    | No       | No    
 6 | repo-debug-update-non-oss | openSUSE-13.2-Update-Debug-Non-Oss   | No       | No    
 7 | repo-non-oss                            | openSUSE-13.2-Non-Oss                              | Yes      | No    
 8 | repo-oss                                    | openSUSE-13.2-Oss                                       | Yes      | No    
 9 | repo-source                              | openSUSE-13.2-Source                                 | No        | No    
10 | repo-update                            | openSUSE-13.2-Update                                 | Yes      | No    
11 | repo-update-non-oss            | openSUSE-13.2-Update-Non-Oss                | Yes      | No  
12 | shells                                        | shells                                                                 | Yes      | No

The leading number in the repos would suffice in our example. In this example we will do a vendor change from OBS shell repo to standard openSUSE repo.

List the packages from the shell repo (leading number 12)

  •  zypper --no-refresh se -t package -ir 12 | awk '$1 ~ /^i/{print $3}'

The --no-refresh: literal meaning no need to explain.
The -t package : type is a package
The se means: search
The i means: installed-only
The r means: repo and its arguments are <aliase|repo|url> which zypper will ONLY search for packages.


Replace the   {print $3}   with    {printf("<%s>\n", $3)}   in the awk code to check for white spaces in the packages names.

  •  zypper --no-refresh se -t package -ir 12 | awk '$1 ~ /^i/{printf("<%s>\n", $3)}'


That looks good so far since the     <   and   >   does not show any white space before and after the package names.
Now capture/save that packages in an (bash) array named packages in our example.

  •  mapfile -t packages < <(zypper --no-refresh se -t package -ir 12 | awk '$1 ~ /^i/{print $3}') 

The previous code is bash which requires version 4 and up. Now if you're stuck with bash3 (which is highly unlikely but hey :) ) you can do a while read loop and save the output in an array.

  while read -r package; do 
  done < <(zypper --no-refresh se -t package -ir 12 | awk '$1 ~/^i/{print $3}') 

Note that  package   and    packages   are not the same and it is arbitrary.

Now disable that shell repo (leading number 12).

  •  zypper mr -d 12 

Do a force reinstall of the packages that came from the OBS shell repo (leading number 12) to the standard openSUSE repo. The idea is, since the OBS repo is disabled then libzypp or zypper does not have a choice but to look for packages at the enabled repos.

  •  zypper in -f "${packages[@]}" 

Thats it, just wait for zypper to tell you that packages are going to be reinstalled.

Additional search for packages using zypper

To list packages

  •  zypper --no-refresh se -t package -ir # 

To list patterns

  •   zypper --no-refresh se -t pattern -ir # 

To list patches

  •   zypper --no-refresh se -t patch -ir #  

where # is the number 12 in the previous example. Adjust/add that modification to the awk code on the previous example and you should be fine.

So far only package and pattern can be force-reinstall ( at least on this side ).

Thursday, December 11, 2014

Curl and download error of packages

Most of the time i am behind a NAT router either at work or at home and those error are pretty common on my openSUSE system.

The old school trick i always do is ping those hosts.



now edit the file /etc/hosts  and add



eg. something like this. 

That trick always saves me from choosing a local mirror manually. It works for me and I'm not saying it will work for everyone, just try it and see.

Monday, April 7, 2014


Some tips howto boot your not so bootable system using the DVD.

Once you are logged in then you can use
 yast2 bootloader 
 fdisk -l  to check for the bootable flag
 grub2-mkconfig /boot/grub2/grub.cfg 

Good Luck folks!

Saturday, March 8, 2014

Disable autorefresh

Disabling auto refresh of your repositories has some benefits. If you are on a limited internet connection or you just do not want to refresh every time you invoke zypper or yast sw_single then this post might be of interest for you.

Disable auto refresh for all remote repositories.
 zypper mr -R -t 

Now if you want to perform an update obviously you need to refresh the repositories. Here is a function that should help you do that. OpenSUSE defaults to bash for the log-in shell so you can just add this to your /etc/bash.bashrc.local  note that  bashrc.local does not exist and needs to be created.

update () {
## Check if user is root, if not exit with an error.    
if (( EUID !=0 )); then
  echo 'Root privileges are required for refreshing system repositories.' 1>&2
  return 1


## Create an array from the enabled repos by parsing zypper lr.    
while IFS="|" read -ra line; do
  [[ ${line[3]} = *Yes* ]] && enabled+=("${line[0]// /}")

done < <(zypper lr) 

## Refresh all enabled repos and update.   
zypper ref "${enabled[@]}" && zypper up

After you have save it you can source bashrc.local run:    source /etc/bash.bashrc.local      and then you can just run:    update 

One question might arise if you change you're log-in shell into something more advance. Don't worry you can just create a bash script and name it    update   (or whatever name you like) and modify that function like this.


## Check if user is root, if not exit with an error.
if (( EUID !=0 )); then
  echo 'Root privileges are required for refreshing system repositories.' 1>&2
  exit 1

## Create an array from the enabled repos by parsing zypper lr.
while IFS="|" read -ra line; do
  [[ ${line[3]} = *Yes* ]] && enabled+=("${line[0]// /}")

done < <(zypper lr)

## Refresh all enabled repos and update.  
zypper ref "${enabled[@]}" && zypper up

Put it in say   /usr/local/bin  or any place that you are comfortable with. Make it executable
  chmod +x update  Then you can just run   update

Happy updating!