Difference between pages "Error messages and their solutions" and "BaBE - Bash By Examples"

From Linuxintro
(Difference between pages)
imported>ThorstenStaerk
 
imported>ThorstenStaerk
 
Line 1: Line 1:
This is a collection of known error messages and their solution. Mostly these error messages result from missing [[dependencies]].
+
<metadesc>BaBE - Bash by Examples; Your significant Linux scripting tutorial: Redirection, Piping, conditional execution, user dialogs, loops, process management, backticks, string replacement, pitfalls and much more.</metadesc>
  
=== a52 ===
+
BaBE - Bash By Examples; Your significant Linux scripting tutorial;;
'''Symptom''', in this case from [[build]]ing [[vlc]]:
 
[[configure]]: error: Could not find liba52 on your system: you may get it from http://liba52.sf.net/. Alternatively you can use --disable-a52 to disable the a52 plugin.
 
'''Solution''', in this case for SUSE Linux:
 
[[yast]] -i liba52-devel
 
  
=== access forbidden ===
+
<pic src="http://www.linuxintro.org/images/Bash-scripting-mindmap.jpg" width=70% align=right caption=MindMap />
'''Symptom''': When surfing to a web site from your [[apache]] web server your browser tells you
 
Access forbidden!
 
 
You don't have permission to access the requested directory. There is either no index document or the directory is read-protected.
 
 
If you think this is a server error, please contact the webmaster.
 
Error 403
 
'''Solution''', in this case for SUSE Linux:
 
In /etc/apache2 search for AllowOverride:
 
[[grep]] -ir "allowoverride" *
 
Make sure all AllowOverride are set to None, then restart your apache:
 
/etc/init.d/apache2 restart
 
  
=== atk ===
+
= Hello world =
'''Symptom''', in this case from gqcam:
+
The easiest way to get your feet wet with a programming language is to start with a program that simply outputs a trivial text, the so-called hello-world-example. Here it is for bash:
  /usr/include/gtk/gtkwidget.h:40:21: fatal error: atk/atk.h: No such file or directory
+
* create a file named hello in your home directory with the following content:
  compilation terminated.
+
  [http://en.wikipedia.org/wiki/Shebang_%28Unix%29 #!/bin/bash]
  make: *** [gqcam.o] Error 1
+
[[echo]] "Hello, world!"
'''Solution''', in this case for SUSE Linux 11.3:
+
* [[open a console]], enter
  [[cp]] -r /usr/include/atk-1.0/atk/ /usr/include/
+
  cd
 +
  chmod +x hello
 +
* now you can execute your file like this:
 +
# ./hello
 +
Hello, world!
 +
* or like this:
 +
  # bash hello
 +
Hello, world!
 +
You see - the output of your shell [[program]] is the same as if you had entered the commands into a console.
  
=== C compiler ===
+
= calling commands =
<pre>
+
In your shell script you can call every command that you can call when [[opening a console]]:
~/freeciv-2.1.9 # ./configure
+
<source>
checking build system type... i686-pc-linux-gnu
+
echo "This is a directory listing, latest modified files at the bottom:"
checking host system type... i686-pc-linux-gnu
+
ls -ltr
checking for a BSD-compatible install... /usr/bin/install -c
+
echo "Now calling a browser"
checking whether build environment is sane... yes
+
firefox
checking for gawk... gawk
+
echo "Continuing with the script"
checking whether make sets $(MAKE)... no
+
</source>
checking whether to enable maintainer-specific portions of Makefiles... no
 
checking for style of include used by make... none
 
checking for gcc... no
 
checking for cc... no
 
checking for cc... no
 
checking for cl... no
 
configure: error: no acceptable C compiler found in $PATH
 
See `config.log' for more details.
 
linux-zcx2:~/freeciv-2.1.9 # gcc
 
If 'gcc' is not a typo you can use command-not-found to lookup the package that contains it, like this:
 
    cnf gcc
 
linux-zcx2:~/freeciv-2.1.9 # yast -i gcc-c++
 
</pre>
 
  
=== C++ compiler ===
+
= variables =
Problem e.g.:
+
== input ==
  CMake Error: your CXX compiler: "CMAKE_CXX_COMPILER-NOTFOUND" was not found.  Please set CMAKE_CXX_COMPILER to a valid compiler path or name.
+
To show you how to deal with variables, we will now write a script that asks for your name and greets you:
'''Solution''', e.g. under Debian:
+
  echo "what is your name? "
aptitude install build-essential
+
read name
 +
echo "hello $name"
 +
You see that the name is stored in a variable $name. Note the quotation marks '''"''' around "hello $name". By using these you say that you want variables to be replaced by their content. If you were to use apostrophes, the name would not be printed, but $name instead.
  
=== C header files ===
+
== ${} ==
'''Symptom:''' When starting [[vmware]] player you get an error message:
+
The ${} operator stands for the variable, there is no difference if you write
  C header files matching your running kernel were not found. Refer to your distribution's documentation for installation instructions.
+
  echo "$name"
'''Solution''', in this case for SUSE Linux 12.1:
+
or
  [[yast]] -i kernel-source
+
  echo "${name}"
 +
So what is the sense of this? Imagine you want to echo a string directly, without any blank, after the content of a variable:
 +
<source>
 +
  echo "if I add the syllable owa to your name it will be ${name}owa"
 +
</source>
  
=== can't open display ===
+
== common mistakes ==
'''Symptom:''' When you call yast2 on a remote computer you get the error message
+
Note that the variable is called $name, however the correct statement to read it from the keyboard is
  terminate called after throwing an instance of 'YUIException'
+
  read name
  what():  Can't open display
+
It is a common mistake to write
  YaST got signal 6 at YCP file Wizard.ycp:699
+
  read $name
'''Solution:''' Log out and log in again using ssh -Y instead of ssh -X
+
which means "read a string and store it into the variable whose name is stored in $name"
  
=== capabilities.h ===
+
= parameters =
'''Symptom''', in this case from [[build]]ing vdr
+
<source>
vdr.c:35:28: fatal error: sys/capability.h: No such file or directory
+
echo "Here are all parameters you called this script with: $@"
'''Solution''', in this case for SUSE 11.3
+
echo "Here is parameter 1: $1"
[[yast]] -i libcap-devel
+
echo "Which parameter do you want to be shown? "
 +
read number
 +
args=("$@")
 +
echo "${args[$number-1]}"
 +
</source>
  
=== cairo ===
+
= return codes =
'''Symptom''', in this case from [[build]]ing gqcam:
+
Every bash script can communicate with the rest of the system by
  /usr/include/gdk/gdkscreen.h:31:19: fatal error: cairo.h: No such file or directory
+
* sending data to [[stdout]]
'''Solution''', in this case for SUSE 11.3:
+
* sending data to [[stderr]]
  [[cp]] /usr/include/cairo/* /usr/include/
+
* delivering a return code
 +
The return code is 0 if everything worked well. You can query it for the most recent command using $?:
 +
<source>
 +
  bootstick@bootstick:~$ echo "hello world"; echo $?
 +
hello world
 +
0
 +
  bootstick@bootstick:~$ echo "hello world">/proc/cmdline; echo $?
 +
bash: /proc/cmdline: Permission denied
 +
1
 +
</source>
  
=== certificate.pem ===
+
In bash, true is 0 and false is any value but 0. There exist two commands, true and false that deliver true or false, respectively:
'''Symptom:''' A program, e.g. [[sHellinaBox]] outputs the error message:
+
bootstick@bootstick:~$ true; echo $?
  Cannot read valid certificate from "certificate.pem". Check file permissions and file format.
+
0
 +
bootstick@bootstick:~$ false; echo $?
 +
  1
  
'''Reason,''' in this case for [[sHellInaBox]]: The program cannot create certificate.pem in the current folder
+
= line feeds =
 +
Let's look at the following script:
 +
read name
 +
if [ $name = "Thorsten" ]; then echo "I know you"; fi
 +
Instead of a semicolon you can write a line feed like this:
 +
read name
 +
if [ $name = "Thorsten" ]
 +
  then echo "I know you"
 +
fi
 +
And instead of a line feed you can use a semicolon:
 +
read name; if [ $name = "Thorsten" ]; then echo "I know you"; fi
 +
If you want to insert a line feed where you do not need one, e.g. to make the code better readable, you must prepend it with a backslash:
 +
read \
 +
  name
 +
if [ $name = "Thorsten" ]
 +
  then \
 +
    echo "I know you"
 +
fi
  
'''Solution,''' in this case for [[shellInABox]]: start the program in /tmp
+
= storing a command's output =
 +
To read a command's output into a variable use $(), backticks or [[piping]].
  
=== clock_gettime ===
+
== $() ==
'''Symptom''', in this case from [[building]] http://svn.icmb.utexas.edu/svn/repository/trunk/zpub/sdkpub/usbkey_dlpd/macosx/d2xx/Samples/EEPROM/write/main.c:
+
  arch=$(uname -m)
  linux_usbfs.c:(.text+0x182): undefined reference to `clock_gettime'
+
echo "Your system is a $arch system."
'''Reason:''' You are missing the realtime library
 
  
'''Solution:''' Include the realtime library into your compile options, add the paramter
+
== backticks ==
  -lrt
+
  arch=`uname -m`
 +
echo "Your system is a $arch system."
  
=== confuse.h ===
+
== piping ==
'''Symptom:''' When [[build]]ing a software you get an error message like
+
[[Piping]] is a very elegant concept in the Linux world. It allows you to take one command's output and use it as input for the next command. Now you can divide tasks into smaller tasks. For example instead of having a program counting all files in a directory you use one program (ls) to ''list'' all files in a directory and one program (wc) to count the lines:
  callbacks.c:10:21: fatal error: confuse.h: No such file or directory
+
<source>
 +
  ls | wc -l
 +
</source>
 +
You can also put the output into a variable, in this case $arch:
 +
<source>
 +
uname -m | while read arch; do echo "Your system is a $arch system."; done
 +
</source>
  
'''Solution,''' in this case for SUSE Linux:
+
== comparison ==
  [[yast]] -i libconfuse-devel
+
The advantage of using backticks over $() is that backticks also work in the sh shell. The advantage of using $() over backticks is that you can cascade them. In the example below we use this possibility to get a list of all files installed with rpm on the system:
 +
  filelist=$([[rpm]] -ql $(rpm -qa))
 +
You can use the piping approach if you need to cascade in sh, but this is not focus of this bash tutorial.
  
=== curl ===
+
== common mistakes ==
;Symptom: When [[build]]ing a software you get an error message like
+
Usually unexperienced programmers try something like
  configure: error: libcurl development files required
+
  uname -m | read arch
;Solution: Install the development package for libcurl, e.g. for SUSE:
+
which [http://mywiki.wooledge.org/BashFAQ/024 does not work]. You must embed the read into a while loop.
yast -i libcurl-devel
 
  
=== DBUS ===
+
= conditions =
'''Symptom''', in this case from [[build]]ing [[vlc]]:
+
The easiest form of a condition in bash is this '''if''' example:
  configure: error: Couldn't find DBus >= 1.0.0, install libdbus-dev ?
+
echo "what is your name? "
'''Solution''', in this case for SUSE 11.3:
+
read name
[[yast]] -i dbus-1-devel
+
if [ $name = "Thorsten" ]; then echo "I know you"; fi
 +
Now let's look closer at this, why does it work? Why is there a blank needed behind the [ sign? The answer is that [ is just an ordinary [[command]] in the shell. It delivers a return code for the expression that follows till the ] sign. To prove this we can write a script:
 +
<source>
 +
  if true; then echo "the command following if true is being executed"; fi
 +
if false; then echo "this will not be shown"; fi
 +
</source>
  
=== dlopen ===
+
== empty strings ==
'''Symptom''', in this case from [[build]]ing http://svn.icmb.utexas.edu/svn/repository/trunk/zpub/sdkpub/usbkey_dlpd/macosx/d2xx/Samples/EEPROM/write/main.c
+
An empty string evaluates to false inside the [ ] operators so it is possible to check if a string ''result'' is empty like this:
  ftd2xx.c:(.text+0x2cb): undefined reference to `dlopen'
+
  # result=
'''Solution''', in this case for SUSE 12.1:
+
# if [ $result ]; then echo success; fi
Add
+
# result=good
  -ldl
+
  # if [ $result ]; then echo success; fi
to your compile parameters.
+
success
  
=== firefox ===
+
== arithmetic expressions ==
'''Symptom:''' When trying to start [[firefox]] you get a message:
+
You can compare integer numbers like this:
  Firefox is already running, but is not responding. To open a new window, you must first close the existing Firefox process, or restart your system.
+
  echo "what is your age? "
 +
read age
 +
if (( $age >= 21 )); then echo "Let's talk about sex."; fi
 +
However bash does not understand floating point numbers. To compare floating numbers you will use external programs such as bc:
 +
<source>
 +
$ if [ $(echo "2.1<2.2"|bc) = 1 ]; then echo "correct"; else echo "wrong"; fi
 +
correct
 +
$ if [ $(echo "2.1>2.2"|bc) = 1 ]; then echo "correct"; else echo "wrong"; fi
 +
wrong
 +
</source>
  
'''Solution''', in this case for SUSE Linux 12.1:
+
== not equal ==
[[open a console]] and enter the [[command]]
+
To check if a variable is NOT equal to whatever, use !=:
killall firefox-bin
+
<source>
 +
if [ "$LANG" != "C" ]; then echo "please set your system lanugage to C"; fi
 +
</source>
  
=== fribidi ===
+
== common mistakes ==
'''Symptom''', in this case from [[build]]ing [[vlc]]:
+
Common mistakes are:
[[configure]]: error: Package requirements (fribidi) were not met:
+
* to forget the blank behind/before the [ or ] character
+
* to forget the blank behind/before the equal sign
No package 'fribidi' found
+
* see [[what does "unary operator expected" mean]]
 
Consider adjusting the PKG_CONFIG_PATH environment variable if you
 
installed software in a non-standard prefix.
 
 
Alternatively, you may set the environment variables FRIBIDI_CFLAGS
 
and FRIBIDI_LIBS to avoid the need to call pkg-config.
 
See the pkg-config man page for more details.
 
'''Solution''', in this case for SUSE 11.3:
 
[[yast]] -i fribidi-devel
 
  
=== ftp: local: whatever.log: Operation not permitted ===
+
= Redirections =
'''Symptom:''' you want to download a file using e.g.
+
To redirect the output of a [[command]] to a file you have to consider that there are two output streams in UNIX, [[stdout,stderr and stdin|stdout and stderr]].
ftp -p ftp://user:password@server/tmp/whatever.log
 
and get an error message
 
ftp: local: whatever.log: Operation not permitted
 
There is already a file whatever.log in your current working directory. You do not have write access to this file.
 
  
'''Reason:''' The command tries to overwrite your local ''whatever.log'' and fails because of missing privileges.
+
= filling files =
 +
To create a file, probably the easiest way is to use [[cat]]. The following example writes text into README till a line occurs that only contains the string "EOF":
 +
cat >README<<EOF
 +
This is line 1
 +
This is line 2
 +
This is the last line
 +
EOF
 +
Afterwards, README will contain the 3 lines below the cat command and above the line with EOF.
  
'''Solution:''' Delete the local file ''whatever.log'' as root:
+
= loops =
rm ''whatever.log''
 
  
=== gcrypt ===
+
== for loops ==
'''Symptom''', in this case from [[build]]ing [[vlc]]:
+
Here is an example for a for-loop. It makes a [[backup]] of all text files:
configure: error: libgcrypt version 1.1.94 or higher not found. Install libgcrypt or use --disable-libgcrypt. Have a nice day.
+
for i in *.txt; do [[cp]] $i $i.bak; done
'''Solution''', in this case for SUSE 11.3:
+
The above command takes each .txt file in the current directory, stores it in the [[variable]] $i and copies it to $i.bak. So ''file''.txt gets copied to ''file''.txt.bat.
[[yast]] -i libgcrypt-devel
 
  
=== gdk ===
+
You can also use subsequent numbers as a for loop using the command seq like this:
'''Symptom''', in this case from [[build]]ing gqcam:
+
for i in $([[seq]] 1 1 3); do [[echo]] $i; done
/usr/include/gtk/gtk.h:32:21: fatal error: gdk/gdk.h: No such file or directory
 
'''Solution''', in this case for SUSE Linux 11.3:
 
[[cp]] -pr /usr/include/gtk-2.0/gdk /usr/include/
 
  
=== gdkconfig ===
+
== while loops ==
'''Symptom''':
+
  $ while true; do read line; done
/usr/include/gdk/gdktypes.h:55:23: fatal error: gdkconfig.h: No such file or directory
 
compilation terminated.
 
[[make]]: *** [gqcam.o] Error 1
 
'''Solution''':
 
linux-noqb:~/gqcam-0.8 # cd /usr/include/
 
linux-noqb:/usr/include # find -iname "gdkconfig*"
 
linux-noqb:/usr/include # cd /usr/lib64/
 
linux-noqb:/usr/lib64 # find -iname "gdkconfig*"
 
./gtk-2.0/include/gdkconfig.h
 
  linux-noqb:/usr/lib64 # cp /usr/lib64/gtk-2.0/include/gdkconfig.h /usr/include/
 
  
=== gdk-pixbuf ===
+
= negations =
'''Symptom''':
+
You can negate a result with the ! operator. $? is the last command's return code:
  /usr/include/gdk/gdkpixbuf.h:37:35: fatal error: gdk-pixbuf/gdk-pixbuf.h: No such file or directory
+
  # true
  compilation terminated.
+
  # echo $?
  [[make]]: *** [gqcam.o] Error 1
+
  0
'''Solution''':
+
# false
<pre>
+
# echo $?
linux-noqb:~/gqcam-0.8 # cd /usr/include/
+
1
linux-noqb:/usr/include # find -iname "gdk-pixbuf*"
+
# ! true
./gtk-2.0/gdk-pixbuf
+
# echo $?
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-enum-types.h
+
1
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-animation.h
+
# ! false
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-features.h
+
# echo $?
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-core.h
+
0
./gtk-2.0/gdk-pixbuf/gdk-pixbuf.h
+
So you get an endless loop out of:
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-transform.h
+
while ! false; do echo hallo; done
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-io.h
 
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-marshal.h
 
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-loader.h
 
./gtk-2.0/gdk-pixbuf/gdk-pixbuf-simple-anim.h
 
./gtk-2.0/gdk-pixbuf-xlib
 
./gtk-2.0/gdk-pixbuf-xlib/gdk-pixbuf-xlib.h
 
./gtk-2.0/gdk-pixbuf-xlib/gdk-pixbuf-xlibrgb.h
 
linux-noqb:/usr/include # cp -r /usr/include/gtk-2.0/gdk-pixbuf /usr/include/
 
</pre>
 
  
=== gettext ===
+
The following code checks the file /tmp/success to contain "success". As long as this is ''not'' the case it continues checking:
'''Symptom''', in this example from [[build]]ing [[pidgin]]:
+
  while ! (grep "success" /tmp/success)
  [[configure]]: error: GNU gettext tools not found; required for intltool
+
do
'''Solution''', in this case for SLES 11:
+
  sleep 30
  # [[yast]] -i gettext-tools
+
  done
  
=== gio ===
+
The following code checks if the file dblog.log exists. As long as this is not the case it tries to download it via ftp:
'''Symptom''', in this case [[build]]ing from gqcam:
+
<source>
  /usr/include/gdk/gdkapplaunchcontext.h:30:21: fatal error: gio/gio.h: No such file or directory
+
  while ! (test -e dblog.log); do
'''Solution''', in this case for SUSE Linux 11.3:
+
  ftp -p ftp://user:password@server/tmp/dblog.log >/dev/null
  [[cp]] -r /usr/include/glib-2.0/gio/ /usr/include/
+
  echo -en "."
 +
  sleep 1
 +
  done
 +
</source>
  
=== glib ===
+
== common mistakes ==
'''Symptom''', in this case from [[build]]ing [[xawtv]]:
+
* bash is very picky regarding spaces. There MUST be a space after the ! if it means negation.
error: glib.h: No such file or directory
 
'''Solution''' (in this case for SUSE 11.3):
 
[[yast]] -i glib2-devel
 
[[cp]] /usr/include/glib-2.0/glib.h /usr/include/
 
cp -pr /usr/include/glib-2.0/glib /usr/include/
 
  
=== glibconfig ===
+
= sending a process to the background =
'''Symptom''', in this case from [[build]]ing gqcam:
+
To send a process to the [[background]], use the ampersand sign (&):
  /usr/include/glib/gtypes.h:34:24: fatal error: glibconfig.h: No such file or directory
+
  firefox & echo "Firefox has been started"
'''Solution''', in this case for SUSE 11.3:
+
You see a newline is not needed after the &
[[cp]] /usr/lib64/glib-2.0/include/glibconfig.h /usr/include/
 
  
=== gmodule ===
+
= forking a process =
'''Symptom''', in this case from [[build]]ing gqcam:
+
You can build a process chain using parantheses. This is useful if you want to have two instruction streams being executed in parallel:
/usr/include/gio/giomodule.h:31:21: fatal error: gmodule.h: No such file or directory
+
<source>
'''Solution''', in this case for SUSE 11.3:
+
  (find -iname "helloworld.txt") & (sleep 5; echo "Timeout exceeded, killing process"; kill $!)
  [[cp]] /usr/include/glib-2.0/gmodule.h /usr/include/
+
</source>
  
=== gtk ===
+
= functions =
'''Symptom''', in this case from [[build]]ing [[kino]]:
+
To define a function in bash, use a non-keyword and append opening and closing parentheses. Here a function greet is defined and it prints "Hello, world!". Then it is called:
checking for GTK2... configure: error: Package requirements (gthread-2.0 libglade-2.0 >= 2.5.0 gtk+-2.0 >= 2.6) were not met:  
+
  # greet()
   
+
  {
  No package 'libglade-2.0' found
+
    echo "Hello, world"
'''Solution''', in this case for SUSE 11.3:
+
}
  [[yast]] -i libglade2-devel
+
  # greet
  
=== gtk-config ===
+
If you hand over parameters you can greet any planet you like:
'''Symptom''', in this case from [[build]]ing gqcam:
+
# greet()
  /bin/sh: gtk-config: command not found
+
{
  gqcam.c:32:21: fatal error: gtk/gtk.h: No such file or directory
+
    echo "Hello, $1"
'''Solution''', in this case for SUSE 11.3:
+
  }
* install gtk 2.20
+
  # greet Mars
* copy the header files
+
Hello, Mars
  [[cp]] -pr /usr/include/gtk-2.0/gtk/ /usr/include
+
# greet World
 +
  Hello, World
  
=== gtk-window-dialog ===
+
= react on CTRL_C =
<pre>
+
The command trap allows you to trap CTRL_C keystrokes so your script will not be aborted
frontend.c:411:44: error: ‘GTK_WINDOW_DIALOG’ undeclared (first use in this function)
+
<source>
frontend.c:411:44: note: each undeclared identifier is reported only once for each function it appears in
+
#!/bin/bash
make: *** [frontend.o] Error 1
 
linux-noqb:~/gqcam-0.8 # cd
 
[1]+  Exit 16                yast2 sw_single  (wd: ~/gqcam-0.8)
 
(wd now: ~)
 
linux-noqb:~ # cd gtk+-2.20.1/
 
linux-noqb:~/gtk+-2.20.1 # grep -ri "gtk_window_dialog" *
 
ChangeLog.pre-1-0:      GTK_WINDOW_DIALOG as a destination for reparenting the child of
 
ChangeLog.pre-2-0:      * gtk/testgtk.c (dnd_drop): remove use of GTK_WINDOW_DIALOG
 
ChangeLog.pre-2-0:      * gtk/gtkcompat.h (GTK_WINDOW_DIALOG): compat #define
 
ChangeLog.pre-2-0:      GTK_WINDOW_DIALOG GTK_WINDOW_TOPLEVEL
 
ChangeLog.pre-2-0:      * gtk/gtkenums.h (enum GtkWindowType): remove GTK_WINDOW_DIALOG
 
</pre>
 
  
=== intltool ===
+
trap shelltrap INT
'''Symptom''', in this case from [[build]]ing [[pidgin]]:
 
[[configure]]: error: The intltool scripts were not found. Please install intltool.
 
'''Solution''', in this case with SLES 11:
 
# [[wget]] http://ftp.gnome.org/pub/gnome/sources/intltool/0.35/intltool-0.35.5.tar.bz2
 
# bunzip2 intltool-0.35.5.tar.bz2
 
# [[tar]] xvf intltool-0.35.5.tar
 
# [[cd]] intltool-0.35.5/
 
# ./[[configure]] && [[make]] -j8 && make [[install]]
 
  
=== jni ===
+
shelltrap()
'''Symptom''', in this case from compiling soprano:
+
{
-- Could NOT find JNI (missing:  JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH)
+
    echo "You pressed CTRL_C, but I don't let you escape"
CMake Error at CMakeLists.txt:84 (file):
+
}
  file Internal CMake error when trying to open file:
 
  /usr/lib64/jvm/java-1.7.0-openjdk/jni.h for reading.
 
'''Solution''': Install the development toolkit for java, in this case for SUSE 12.2:
 
java-1_7_0-openjdk-devel
 
  
=== jpeg ===
+
while true; do read line; done
'''Symptom''', in this case from [[xawtv]]:
+
</source>
Oops:  jpeg library not found.  You need this one, please install.
 
'''Solution''', in this case for SUSE 11.3:
 
[[yast]] -i libjpeg-devel
 
  
=== KDE ===
+
;Note: You can still ''pause'' your script by pressing CTRL_Z, send it to the [[background]] and kill it there. To catch CTRL_Z, replace INT by TSTP in the above example. To get an overview of all signals that you might be able to trap, [[open a console]] and enter
'''Symptom''':
+
kill -l
  ERROR: Could not find KDE4 kde4-config
 
'''Solution''', e.g. under Debian:
 
apt-get [[install]] kdelibs5-dev
 
  
=== libavcodec ===
+
= helpful programs =
'''Symptom''', in this case from [[build]]ing [[vlc]]:
+
== awk: read a specific column ==
configure: error: No package 'libavcodec' found
+
[[awk]] is a program that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can extract columns from a text. Let's imagine you want to use the [[program]] [[vmstat]] to find out how high the CPU user load was. Here is the output from vmstat:
'''Solution''', in this case for SUSE Linux Enterprise Server 11:
 
[[yast]] -i lib[[ffmpeg]]-devel
 
  
=== liblavdisplay ===
+
<pic src="http://www.linuxintro.org/images/vmstat.png" align=text width=100% caption=VmStat />
'''Symptom''', e.g. when compiling mjpegtools:
 
./.libs/liblavplay.so: undefined reference to `XOpenDisplay'
 
'''Solution''', e.g. under SUSE:
 
[[yast]] -i libSDL-devel
 
  
=== libQtDBus ===
+
We see the user load is in colum 13, and we only want to print this column. We do it with the following command:
'''Symptom''', in this case from running [[skype]]:
+
  vmstat 5 | awk '{print $13}'
  skype: error while loading shared libraries: libQtDBus.so.4: cannot open shared object file: No such file or directory
+
This will print a line from vmstat all 5 seconds and only write the column 13. It looks like this:
'''Reason''': You do not have the 32bit libraries for Qt.
+
# vmstat 5 | awk '{print $13}'
 +
 +
us
 +
1
 +
1
 +
0
 +
1
 +
To store the CPU user load into a variable we use
 +
load=$(vmstat 1 2 | tail -n 1 | awk '{print $13}')
 +
What happens here? First vmstat outputs some data in its first line. The data about CPU load can only be rubbish because it did not have any time to measure it. So we let it output 2 lines and wait 1 second between them ( => vmstat 1 2 ). From this command we only read the last line ( => tail -n 1 ). From this line we only print column 13 ( => awk '{print $13}' ). This output is stored into the variable $load ( => load=$(...) ).
  
'''Solution''', in this case for SUSE 11.3:
+
== grep: search a string ==
[[yast]] -i libqt4-32bit
+
[[grep]] is a [[program]] that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can extract lines that contain a string or match a [[regex]] pattern. Let's imagine you want all external links from www.linuxintro.org's main page:
 +
wget -O linuxintro.txt http://www.linuxintro.org
 +
grep "http:" linuxintro.txt
  
=== libfontconfig ===
+
== sed: replace a string ==
'''Symptom''', in this case from building [[xawtv]]:
+
[[sed]] is a [[program]] that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can replace a string by another one. Let's imagine you want to print your distribution's name, but lsd_release outputs too much:
  /usr/bin/ld: cannot find -lfontconfig
+
  # lsb_release -d
'''Solution''', in this case for Ubuntu 11.10:
+
Description:   openSUSE 12.1 (x86_64)
  apt-get install libfontconfig1-dev
+
You want to remove this string "Description" so you replace it by nothing:
 +
  lsb_release -d | sed "s/Description:\t//"
 +
openSUSE 12.1 (x86_64)
  
=== libgdk ===
+
Once you understand [[regular expressions]] you can use sed with them:
'''Symptom''', in this case from running realplay:
 
/opt/real/RealPlayer/realplay.bin: error while loading shared libraries: libgdk-x11-2.0.so.0: cannot open shared object file: No such file or directory
 
'''Reason''': For SUSE Linux, libgdk-x11-2.0.so.0 is provided by the [[package]] libgtk. /usr/lib64/libgdk-x11-2.0.so.0 is provided by the package libgtk-2_0-0-2.24.7-2.5.1.x86_64:
 
# [[rpm]] -qf /usr/lib64/libgdk-x11-2.0.so.0
 
libgtk-2_0-0-2.24.7-2.5.1.x86_64
 
/usr/lib/libgdk-x11-2.0.so.0 is provided by the package libgtk-2_0-0-32bit:
 
rpm -qf /usr/lib/libgdk-x11-2.0.so.0
 
libgtk-2_0-0-32bit-2.24.7-2.5.1.x86_64
 
'''Solution''', in this case for SUSE 12.1:
 
[[yast]] -i libgtk-2_0-0-32bit
 
  
=== libQt ===
+
* to replace protocol names for a given port (in this case 3200) in /etc/services:
'''Problem''', in this case from running [[umtsmon]]:
+
  sed -ri "s/.{16}3200/sapdp00 3200/" /etc/services
  ./umtsmon: error while loading shared libraries: libqt-mt.so.3: cannot open shared object file: No such file or directory
+
* if you have an [[apache]] [[web server]] here's how you get the latest websites that have been requested:
'''Solution''', in this case for SUSE 11.3:
+
<source>
  [[yast]] -i qt3-32bit
+
  cat /var/log/apache2/access_log | sed ";.*\(GET [^\"]*\).*;\1;"
 +
</source>
  
=== libQtGui ===
+
== tr: replace linebreaks ==
'''Problem''', in this case from running [[skype]]:
+
[[sed]] is a [[program]] that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can replace a character by another one, even over line breaks.
  skype: error while loading shared libraries: libQtGui.so.4: cannot open shared object file: No such file or directory
+
For example here is how you remove all empty lines from your processor information:
'''Reason''': You do not have the 32bit libraries for Qt.
+
<source>
 +
  # cat /proc/cpuinfo | while read a; do ar=$(echo -n $a|tr '\n' ';')
 +
if [ "$ar" <> ";" ]; then echo "$ar"; fi; done
 +
</source>
  
'''Solution''':
+
== wc: count ==
  [[yast]] -i libqt4-x11-32bit
+
With the command wc you can count words, characters and lines. wc -l counts lines. For example to find out how many semicolons are in a line, use the following statement:
 +
while read line
 +
do echo "$line" | tr '\n' ' ' | sed "s/;/\n/g" | wc -l
 +
  done
 +
It lets you input a line of text, counts the semicolons in it and outputs the number.
  
=== libXaw ===
+
How does it do this?
'''Symptom''', in this case from [[build]]ing [[xawtv]]:
 
/usr/bin/ld: cannot find -lXaw
 
'''Solution''', in this case for Ubuntu 11.10:
 
apt-get [[install]] libxaw7-dev
 
  
=== libXext ===
+
It reads lines from your keyboard (while read line). It outputs the line (echo "$line"), but it does not output it in the console. The pipe (|) redirects the output to the input stream of the command tr. The command tr replaces the ENTER ('\
'''Symptom''', in this case from [[build]]ing [[xawtv]]:
+
') by a space (' '). The pipe (|) redirects the output to the input stream of sed. sed substitutes ("s/) the semicolon (;) by (/) a linefeed (\
/usr/bin/ld: cannot find -lXext
+
), globally (/g"). The pipe redirects the output to the input stream of the wc -l command that outputs the count of lines.
'''Solution''', in this case for Ubuntu 11.10:
 
apt-get [[install]] libxext-dev
 
  
=== libXm ===
+
== dialog: create dialogs ==
'''Symptom:''' When installing an rpm (in this case ICAClient) you get an error like
+
Dialog is a command that helps you creating dialogs in the shell. The answers given by the user are send to [[stderr]] and/or influence the command's return code. For example if you run this script:
  error: Failed dependencies:
+
#!/bin/bash
        libXm.so.4 is needed by ICAClient-11.0-1.i386
+
if (dialog --title "Message"  --yesno "Are you having fun?" 6 25)
'''Solution:''' Install openmotif-libs, best for 32bit and 64bit.
+
then echo "glad you have fun"
 +
  else echo "sad you don't have fun"
 +
fi
 +
It will display this dialog:
  
You can find out what package a file belongs to after installing the rpm like this:
+
[[File:snapshot-dialog.png]]
[[rpm]] -qf /usr/lib64/libXm.so.4
 
openmotif-libs-2.3.1-3.13
 
  
=== libXv ===
+
This has been taken from http://www.linuxjournal.com/article/2807. Read there for more info.
'''Symptom''', in this case from [[install]]ing realplayer:
 
# [[rpm]] -ivh Downloads/RealPlayer11GOLD.rpm
 
error: Failed dependencies:
 
        libXv.so.1 is needed by realplay-11.0.2.1744-1.i386
 
'''Solution''', in this case for SUSE Linux:
 
# [[yast]] -i xorg-x11-libXv-32bit
 
  
=== libxml ===
+
= See also =
'''Symptom''', in this case from [[build]]ing [[xawtv]]:
+
* [[bash]]
libxml/parser.h: No such file or directory
+
* [[shell]]
'''Solution''',  in this case for SUSE 11.3:
+
* [[scripting]]
[[yast]] -i libxml-devel
+
* [[bash operators]]
 
+
* http://en.wikibooks.org/wiki/Bash_Shell_Scripting
=== libxml 2 ===
+
* http://ryanstutorials.net/linuxtutorial/scripting.php
'''Symptom''', in this case from [[build]]ing [[xawtv]]:
+
* http://wiki.linuxquestions.org/wiki/Bash_tips
Package libxml-2.0 was not found in the pkg-config search path.
+
* http://wiki.linuxquestions.org/wiki/Bash
Perhaps you should add the directory containing `libxml-2.0.pc'
+
* http://linuxconfig.org/Bash_scripting_Tutorial
to the PKG_CONFIG_PATH environment variable
+
* http://steve-parker.org/sh/intro.shtml
No package 'libxml-2.0' found
+
* http://mywiki.wooledge.org/BashFAQ
'''Solution''', in this case for SUSE 11.3:
+
* http://mywiki.wooledge.org/BashPitfalls
[[yast]] -i libxml2-devel
 
 
 
=== libXp ===
 
'''Symptom''', in this case from [[build]]ing [[xawtv]]:
 
/usr/bin/ld: cannot find -lXp
 
'''Solution''', in this case for Ubuntu 11.10:
 
apt-get [[install]] libxp-dev
 
 
 
=== libpng ===
 
'''Symptom''', in this case from [[build]]ing [[xawtv]]:
 
/usr/lib64/gcc/x86_64-suse-linux/4.5/../../../../x86_64-suse-linux/bin/ld: cannot find -lpng
 
collect2: ld returned 1 exit status
 
make: *** [console/scantv] Error 1
 
 
 
'''Solution''', in this case for SUSE 11.3:
 
linux-fhbd:~/xawtv # [[yast]] -i libpng14-devel
 
 
 
=== lua ===
 
'''Symptom''', in this case from [[build]]ing [[vlc]]:
 
configure: error: Could not find lua. Lua is needed for some interfaces (rc, telnet, http) as well as many other custom scripts. Use --disable-lua to ignore this error.
 
'''Solution''', in this case for SUSE 11.3:
 
[[yast]] -i lua-devel
 
 
 
=== mad ===
 
'''Symptom''', in this case from [[build]]ing [[vlc]]:
 
configure: error: Could not find libmad on your system: you may get it from http://www.underbit.com/products/mad/. Alternatively you can use --disable-mad to disable the mad plugin.
 
'''Solution''', in this case for SUSE Linux Enterprise Server 11:
 
[[yast]] -i libmad-devel
 
 
 
=== ncurses ===
 
'''Symptom''', in this case from [[build]]ing xawtv:
 
Oops: (n)curses library not found.  You need this one, please install.
 
'''Solution''', in this case for SUSE 11.3:
 
[[yast]] -i ncurses-devel
 
 
 
=== Net/DAV/Server.pm ===
 
'''Symptom:''' Trying to run a [[perl]] [[program]] you get the message:
 
Can't locate Net/DAV/Server.pm in @INC (@INC contains
 
 
 
'''Reason:''' Perl has a library of functions, and the module Net/DAV/Server.pm is missing there.
 
 
 
'''Solution:''' Install Net/DAV/Server.pm as described under [[cpan]].
 
 
 
=== pango ===
 
'''Symptom''':
 
<pre>
 
/usr/include/gdk/gdktypes.h:37:25: fatal error: pango/pango.h: No such file or directory
 
compilation terminated.
 
make: *** [gqcam.o] Error 1
 
linux-noqb:~/gqcam-0.8 # cd /usr/include/
 
linux-noqb:/usr/include # find -iname "pango*"
 
./pango-1.0
 
./pango-1.0/pango
 
./pango-1.0/pango/pango-break.h
 
./pango-1.0/pango/pangoft2.h
 
./pango-1.0/pango/pango.h
 
[...]
 
</pre>
 
'''Solution''':
 
linux-noqb:/usr/include # cp -r /usr/include/pango-1.0/pango/ /usr/include/
 
 
 
=== popt ===
 
'''Symptom''', in this case from building gphoto2:
 
* Cannot autodetect popt.h
 
'''Solution''', in this case for SUSE Linux:
 
[[yast]] -i popt-devel
 
 
 
=== pthread_create ===
 
'''Symptom''', in this case from [[building]] http://svn.icmb.utexas.edu/svn/repository/trunk/zpub/sdkpub/usbkey_dlpd/macosx/d2xx/Samples/EEPROM/write/main.c
 
undefined reference to `pthread_create'
 
'''Solution''', in this case for SUSE Linux 12.1:
 
Add
 
-lpthread
 
to your [[compile]] parameters
 
 
 
=== python.h ===
 
'''Symptom''', in this case from setting up [[uniconvertor]]:
 
src/modules/filter/streamfilter.c:24:20: fatal error: Python.h: No such file or directory
 
'''Solution''', in this case for SUSE Linux 12.1:
 
[[yast]] -i python-devel
 
 
 
=== qt ===
 
'''Symptom''', in this case from [[build]]ing [http://en.wikipedia.org/wiki/Quassel quassel]:
 
CMake Error at cmake/modules/FindQt4.cmake:1257 (MESSAGE):
 
  Qt qmake not found!
 
 
 
'''Reason''': You are missing the qt build environment
 
 
 
'''Solution''', in this case for SUSE Linux 11.4:
 
[[yast]] -i libqt4-devel
 
 
 
=== serial ===
 
'''Symptom:''' when calling a python program you get an error message like this:
 
ImportError: No module named serial
 
 
 
'''Solution:''' Install python's serial module, e.g. for SUSE Linux:
 
yast -i python-pyserial
 
 
 
=== ssh does not work ===
 
'''Symptom:''' when calling a GUI program within an ssh -X session, you get an error message like this:
 
X Error of failed request:  BadAtom (invalid Atom parameter)
 
  Major opcode of failed request:  20 (X_GetProperty)
 
  Atom id in failed request:  0x17
 
  Serial number of failed request:  4
 
  Current serial number in output stream:  4
 
  
'''Solution:''' Exit the session, reconnect with ssh -Y
+
[http://www.facebook.com/sharer.php?u=http%3A%2F%2Fwww.linuxintro.org%2Fwiki%2FShell_scripting_tutorial&t=Shell%20scripting%20Tutorial&src=sp Share on Facebook]
  
=== unknown filesystem smbfs ===
+
<stumbleuponbutton />
'''Symptom:''' When trying to mount a [[sambA]] share that is entered in [[fstab]] using the [[commAnd]]
 
mount -a
 
you get the error messages
 
unknown filesystem smbfs
 
'''Solution:''' replace "smbfs" in /etc/fstab by "cifs"
 
  
=== unary operator expected ===
+
[[Category:Mindmap]]
'''Symptom''' when running a [[program]] you get an error message like
+
[[Category:Learning]]
test.sh: line 4: [: =: unary operator expected
 
 
 
'''Reason''' See [[what does "unary operator expected" mean]].
 
 
 
=== xclock not found ===
 
'''Symptom:''' You cannot call xclock. When you [[open a console]] and do it you get the message
 
xclock: command not found
 
'''Solution:''' [[Install]] xclock's [[package]], in this case with SUSE Linux 12.1:
 
yast -i xorg-x11
 
 
 
=== Xlib ===
 
'''Symptom''', in this case from [[build]]ing [[fsl]]:
 
error: X11/Xlib.h: No such file or directory
 
'''Solution''', in this case for CentOs 6:
 
yum install libX11-devel
 
 
 
=== yasm ===
 
'''Symptom''', in this case from [[build]]ing [[mplayer]]:
 
Error: yasm not found, use --yasm='' if you really want to compile without 
 
'''Solution''', in this case for SUSE Linux 11.3:
 
yast -i [http://en.wikipedia.org/wiki/Yasm yasm]
 
 
 
=== zlib ===
 
'''Symptom''', in this case from [[build]]ing [[freeciv]]:
 
checking for gzgets in -lz... no
 
configure: error: Could not find zlib library.
 
'''Reason:''' Your '''''z'''''ipping library ''zlib'' is not [[install]]ed in a way that you can build [[software]] with [[dependencies]] on it. You need the development [[package]] of zlib.
 
 
 
'''Solution''', in this case for SUSE Linux:
 
[[yast]] -i zlib-devel
 
 
 
= See also =
 
* [[dependencies]]
 
* [[troubleshooting]]
 

Revision as of 08:06, 30 March 2020


BaBE - Bash By Examples; Your significant Linux scripting tutorial;;

MindMap

Hello world

The easiest way to get your feet wet with a programming language is to start with a program that simply outputs a trivial text, the so-called hello-world-example. Here it is for bash:

  • create a file named hello in your home directory with the following content:
#!/bin/bash
echo "Hello, world!"
cd
chmod +x hello
  • now you can execute your file like this:
# ./hello
Hello, world!
  • or like this:
# bash hello
Hello, world!

You see - the output of your shell program is the same as if you had entered the commands into a console.

calling commands

In your shell script you can call every command that you can call when opening a console: <source>

echo "This is a directory listing, latest modified files at the bottom:"
ls -ltr
echo "Now calling a browser"
firefox
echo "Continuing with the script"

</source>

variables

input

To show you how to deal with variables, we will now write a script that asks for your name and greets you:

echo "what is your name? "
read name
echo "hello $name"

You see that the name is stored in a variable $name. Note the quotation marks " around "hello $name". By using these you say that you want variables to be replaced by their content. If you were to use apostrophes, the name would not be printed, but $name instead.

${}

The ${} operator stands for the variable, there is no difference if you write

echo "$name"

or

echo "${name}"

So what is the sense of this? Imagine you want to echo a string directly, without any blank, after the content of a variable: <source>

echo "if I add the syllable owa to your name it will be ${name}owa"

</source>

common mistakes

Note that the variable is called $name, however the correct statement to read it from the keyboard is

read name

It is a common mistake to write

read $name

which means "read a string and store it into the variable whose name is stored in $name"

parameters

<source> echo "Here are all parameters you called this script with: $@" echo "Here is parameter 1: $1" echo "Which parameter do you want to be shown? " read number args=("$@") echo "${args[$number-1]}" </source>

return codes

Every bash script can communicate with the rest of the system by

  • sending data to stdout
  • sending data to stderr
  • delivering a return code

The return code is 0 if everything worked well. You can query it for the most recent command using $?: <source>

bootstick@bootstick:~$ echo "hello world"; echo $?
hello world
0
bootstick@bootstick:~$ echo "hello world">/proc/cmdline; echo $?
bash: /proc/cmdline: Permission denied
1

</source>

In bash, true is 0 and false is any value but 0. There exist two commands, true and false that deliver true or false, respectively:

bootstick@bootstick:~$ true; echo $?
0
bootstick@bootstick:~$ false; echo $?
1

line feeds

Let's look at the following script:

read name
if [ $name = "Thorsten" ]; then echo "I know you"; fi

Instead of a semicolon you can write a line feed like this:

read name
if [ $name = "Thorsten" ]
  then echo "I know you"
fi

And instead of a line feed you can use a semicolon:

read name; if [ $name = "Thorsten" ]; then echo "I know you"; fi

If you want to insert a line feed where you do not need one, e.g. to make the code better readable, you must prepend it with a backslash:

read \
  name
if [ $name = "Thorsten" ]
  then \
    echo "I know you"
fi

storing a command's output

To read a command's output into a variable use $(), backticks or piping.

$()

arch=$(uname -m)
echo "Your system is a $arch system."

backticks

arch=`uname -m`
echo "Your system is a $arch system."

piping

Piping is a very elegant concept in the Linux world. It allows you to take one command's output and use it as input for the next command. Now you can divide tasks into smaller tasks. For example instead of having a program counting all files in a directory you use one program (ls) to list all files in a directory and one program (wc) to count the lines: <source>

ls | wc -l

</source> You can also put the output into a variable, in this case $arch: <source>

uname -m | while read arch; do echo "Your system is a $arch system."; done

</source>

comparison

The advantage of using backticks over $() is that backticks also work in the sh shell. The advantage of using $() over backticks is that you can cascade them. In the example below we use this possibility to get a list of all files installed with rpm on the system:

filelist=$(rpm -ql $(rpm -qa))

You can use the piping approach if you need to cascade in sh, but this is not focus of this bash tutorial.

common mistakes

Usually unexperienced programmers try something like

uname -m | read arch

which does not work. You must embed the read into a while loop.

conditions

The easiest form of a condition in bash is this if example:

echo "what is your name? "
read name
if [ $name = "Thorsten" ]; then echo "I know you"; fi

Now let's look closer at this, why does it work? Why is there a blank needed behind the [ sign? The answer is that [ is just an ordinary command in the shell. It delivers a return code for the expression that follows till the ] sign. To prove this we can write a script: <source>

if true; then echo "the command following if true is being executed"; fi
if false; then echo "this will not be shown"; fi

</source>

empty strings

An empty string evaluates to false inside the [ ] operators so it is possible to check if a string result is empty like this:

# result=
# if [ $result ]; then echo success; fi
# result=good
# if [ $result ]; then echo success; fi
success

arithmetic expressions

You can compare integer numbers like this:

echo "what is your age? "
read age
if (( $age >= 21 )); then echo "Let's talk about sex."; fi

However bash does not understand floating point numbers. To compare floating numbers you will use external programs such as bc: <source>

$ if [ $(echo "2.1<2.2"|bc) = 1 ]; then echo "correct"; else echo "wrong"; fi
correct
$ if [ $(echo "2.1>2.2"|bc) = 1 ]; then echo "correct"; else echo "wrong"; fi
wrong

</source>

not equal

To check if a variable is NOT equal to whatever, use !=: <source>

if [ "$LANG" != "C" ]; then echo "please set your system lanugage to C"; fi

</source>

common mistakes

Common mistakes are:

Redirections

To redirect the output of a command to a file you have to consider that there are two output streams in UNIX, stdout and stderr.

filling files

To create a file, probably the easiest way is to use cat. The following example writes text into README till a line occurs that only contains the string "EOF":

cat >README<<EOF
This is line 1
This is line 2
This is the last line
EOF

Afterwards, README will contain the 3 lines below the cat command and above the line with EOF.

loops

for loops

Here is an example for a for-loop. It makes a backup of all text files:

for i in *.txt; do cp $i $i.bak; done

The above command takes each .txt file in the current directory, stores it in the variable $i and copies it to $i.bak. So file.txt gets copied to file.txt.bat.

You can also use subsequent numbers as a for loop using the command seq like this:

for i in $(seq 1 1 3); do echo $i; done

while loops

$ while true; do read line; done

negations

You can negate a result with the ! operator. $? is the last command's return code:

# true
# echo $?
0
# false
# echo $?
1
# ! true
# echo $?
1
# ! false
# echo $?
0

So you get an endless loop out of:

while ! false; do echo hallo; done

The following code checks the file /tmp/success to contain "success". As long as this is not the case it continues checking:

while ! (grep "success" /tmp/success)
do
  sleep 30
done

The following code checks if the file dblog.log exists. As long as this is not the case it tries to download it via ftp: <source>

while ! (test -e dblog.log); do
  ftp -p ftp://user:password@server/tmp/dblog.log >/dev/null
  echo -en "."
  sleep 1
done

</source>

common mistakes

  • bash is very picky regarding spaces. There MUST be a space after the ! if it means negation.

sending a process to the background

To send a process to the background, use the ampersand sign (&):

firefox & echo "Firefox has been started"

You see a newline is not needed after the &

forking a process

You can build a process chain using parantheses. This is useful if you want to have two instruction streams being executed in parallel: <source>

(find -iname "helloworld.txt") & (sleep 5; echo "Timeout exceeded, killing process"; kill $!)

</source>

functions

To define a function in bash, use a non-keyword and append opening and closing parentheses. Here a function greet is defined and it prints "Hello, world!". Then it is called:

# greet()
{
    echo "Hello, world"
}
# greet

If you hand over parameters you can greet any planet you like:

# greet()
{
    echo "Hello, $1"
}
# greet Mars
Hello, Mars
# greet World
Hello, World

react on CTRL_C

The command trap allows you to trap CTRL_C keystrokes so your script will not be aborted <source>

  1. !/bin/bash

trap shelltrap INT

shelltrap() {

   echo "You pressed CTRL_C, but I don't let you escape"

}

while true; do read line; done </source>

Note
You can still pause your script by pressing CTRL_Z, send it to the background and kill it there. To catch CTRL_Z, replace INT by TSTP in the above example. To get an overview of all signals that you might be able to trap, open a console and enter
kill -l

helpful programs

awk: read a specific column

awk is a program that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can extract columns from a text. Let's imagine you want to use the program vmstat to find out how high the CPU user load was. Here is the output from vmstat:

VmStat

We see the user load is in colum 13, and we only want to print this column. We do it with the following command:

vmstat 5 | awk '{print $13}'

This will print a line from vmstat all 5 seconds and only write the column 13. It looks like this:

# vmstat 5 | awk '{print $13}'

us
1
1
0
1

To store the CPU user load into a variable we use

load=$(vmstat 1 2 | tail -n 1 | awk '{print $13}')

What happens here? First vmstat outputs some data in its first line. The data about CPU load can only be rubbish because it did not have any time to measure it. So we let it output 2 lines and wait 1 second between them ( => vmstat 1 2 ). From this command we only read the last line ( => tail -n 1 ). From this line we only print column 13 ( => awk '{print $13}' ). This output is stored into the variable $load ( => load=$(...) ).

grep: search a string

grep is a program that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can extract lines that contain a string or match a regex pattern. Let's imagine you want all external links from www.linuxintro.org's main page:

wget -O linuxintro.txt http://www.linuxintro.org
grep "http:" linuxintro.txt 

sed: replace a string

sed is a program that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can replace a string by another one. Let's imagine you want to print your distribution's name, but lsd_release outputs too much:

# lsb_release -d
Description:    openSUSE 12.1 (x86_64)

You want to remove this string "Description" so you replace it by nothing:

lsb_release -d | sed "s/Description:\t//"
openSUSE 12.1 (x86_64)

Once you understand regular expressions you can use sed with them:

  • to replace protocol names for a given port (in this case 3200) in /etc/services:
sed -ri "s/.{16}3200/sapdp00 3200/" /etc/services
  • if you have an apache web server here's how you get the latest websites that have been requested:

<source>

cat /var/log/apache2/access_log | sed ";.*\(GET [^\"]*\).*;\1;"

</source>

tr: replace linebreaks

sed is a program that is installed on almost all Linux distributions. It is a good helper for text stream processing. It can replace a character by another one, even over line breaks. For example here is how you remove all empty lines from your processor information: <source>

# cat /proc/cpuinfo | while read a; do ar=$(echo -n $a|tr '\n' ';')
if [ "$ar" <> ";" ]; then echo "$ar"; fi; done

</source>

wc: count

With the command wc you can count words, characters and lines. wc -l counts lines. For example to find out how many semicolons are in a line, use the following statement:

while read line
do echo "$line" | tr '\n' ' ' | sed "s/;/\n/g" | wc -l
done

It lets you input a line of text, counts the semicolons in it and outputs the number.

How does it do this?

It reads lines from your keyboard (while read line). It outputs the line (echo "$line"), but it does not output it in the console. The pipe (|) redirects the output to the input stream of the command tr. The command tr replaces the ENTER ('\ ') by a space (' '). The pipe (|) redirects the output to the input stream of sed. sed substitutes ("s/) the semicolon (;) by (/) a linefeed (\ ), globally (/g"). The pipe redirects the output to the input stream of the wc -l command that outputs the count of lines.

dialog: create dialogs

Dialog is a command that helps you creating dialogs in the shell. The answers given by the user are send to stderr and/or influence the command's return code. For example if you run this script:

#!/bin/bash
if (dialog --title "Message"  --yesno "Are you having fun?" 6 25)
then echo "glad you have fun"
else echo "sad you don't have fun"
fi

It will display this dialog:

Snapshot-dialog.png

This has been taken from http://www.linuxjournal.com/article/2807. Read there for more info.

See also

Share on Facebook