Basic Shell (Console) operation for beginners

Booting, installing, newbie
Message
Author
Bruce B

#201 Post by Bruce B »

Random Backgrounds

I like the way Puppy mixes ROX-Filer and various window managers into a
seamless combination.

I don't use desktop icons much, so I removed ROX-Filer from starting up.
(modified ~/.xinitrc)

For my file manager I have long used Midnight Commander. I use Rox in
some operations. I can still run and use it, without it managing the
desktop.

I use JWM to display the wallpaper. I wrote a script to randomize the
wallpaper between three desktops.

The script is fairly true random selection because I don't have many
wallpapers.

However, if I had a lot of wallpapers, I'd have to modify the script.

This same script could be used as a template script for
randomizing ROX-Filer backgrounds.

Code: Select all

#!/bin/bash

jpgdir="/root/.jwm"

cd $jpgdir

if [ "$1" != "--repeat" ] ; then

    for i in {0..2} ; do
        [ -L $i.jpg ] && rm $i.jpg
    done

fi

for i in *.jpg ; do

    rval=`expr $RANDOM % 3`

    case $rval in

        0)
            [ ! -L $rval.jpg ] && ln -s $i $rval.jpg
        ;;

        1)
            [ ! -L $rval.jpg ] && ln -s $i $rval.jpg
        ;;

        2)
            [ ! -L $rval.jpg ] && ln -s $i $rval.jpg
        ;;

    esac

done

for i in {0..2} ; do

    [ ! -L $i.jpg ] && exec $0 --repeat

done

Xruns=`pidof X`

[ "${#Xruns}" -gt "2" ] && jwm -restart
Below are the commands to insert in ~/.jwm/jwmrc-theme, for it to display
jpg images as the background.

Code: Select all

<Desktops count="3">
  <Desktop>
     <Background type="tile">/root/.jwm/0.jpg</Background>
  </Desktop>

  <Desktop>
     <Background type="tile">/root/.jwm/1.jpg</Background>
   </Desktop>

   <Desktop>
    <Background type="tile">/root/.jwm/2.jpg</Background>
   </Desktop>

</Desktops>
~

Bruce B

#202 Post by Bruce B »

Randomizing a List

If anyone doesn't know how to randomize a list or or make a random list from file extension name criteria, this script will suffice as a demo and tutorial. It is also fun to play with. You can add your own criteria such as .jpg or .mp3.

Code: Select all

#!/bin/bash

echo -n > /tmp/randomplay.tmp

for i in `echo *$1` ; do

    echo $i

done

echo -n "Press any key to display prefixed with random numbers"
read -n 1 a

for i in `echo *$1` ; do

    printf "%05d $i\n" $RANDOM >> /tmp/randomplay.tmp

done

cat /tmp/randomplay.tmp

echo -n "Press any  key to display numbers sorted, names randomized"
read -n 1 a

cat /tmp/randomplay.tmp | sort

echo -n "Press any key display numbers removed, names randomized"
read -n 1 a

cat /tmp/randomplay.tmp | sort  | sed -r 's/^[0-9]+ //'

rm /tmp/randomplay.tmp

exit 0
File attached: randomdemo.zip

EDIT: don't enter * on the command line, it is in the script. To show mp3 files only, say randomdemo .mp3

~
Attachments
randomdemo.zip
(410 Bytes) Downloaded 194 times

turf
Posts: 56
Joined: Mon 21 Mar 2011, 19:20

#203 Post by turf »

im having difficulty understanding variables..

what does $1 mean?

Bruce B

#204 Post by Bruce B »

Command Line Arguments

Q: What does $1 mean

A: $1 is the first argument in a command string

Arg 1 is passed to the shell. The shell is usually programmed to do
something specific with the argument. We can and often do pass many
arguments to the shell.

I wrote a script for you to see how arguments are interpreted by the shell.
Copy the file to somewhere in the PATH. If you don't know what the PATH
is, that would be a good next question.

If this is the case, copy argdemo.zip to /root and run commands in blue.

unzip argdemo.zip (to extract the file)

Then play with it.

./argdemo firstname lastname city state

./argdemo "firstname lastname" "city state"

Note the difference when you quote two arguments.

Play with other text, as you wish, until you understand what these
arguments are about.

If you don't understand the script, I will explain each line to you. Just
ask.

Here are the internals of argdemo

Code: Select all

#!/bin/bash

cnt=0

echo Arg $cnt is $0

for i in "$@" ; do

    cnt=$((cnt +1))

    echo "Arg $cnt is $i"

done

exit 0
Have fun.


Bruce

File attached: argdemo.zip

~
Attachments
argdemo.zip
(259 Bytes) Downloaded 180 times

PupGeek
Posts: 353
Joined: Sun 06 Sep 2009, 11:30

#205 Post by PupGeek »

Turf, traditionally speaking, the variable "$1" handles a parameter passed to the script at runtime. For example, if you write a script that will execute a command or series of commands on a file, there has to be a variable within the script that the filename is passed to. This is where a variable like "$1" comes in. You can find more information on this in section 3.2.5 "Special Parameters" on thispage.

Shep
Posts: 878
Joined: Sat 08 Nov 2008, 07:55
Location: Australia

#206 Post by Shep »

01micko wrote:

Code: Select all

 NEWNAME="`echo "$TRACK" |sed 's/[\(\)]//g'|sed 's/"//g'|sed "s/'$//g"|sed "s/^'//g"`" 
You don't want all those sed processes each with a single argument. They can be combined as multiple arguments to a single process, e.g.,

sed -e 's/[\(\)]//g' -e 's/"//g' -e "s/'$//g" -e "s/^'//g"

or combined into a single argument, e.g.,

sed s/[\(\)]//g'; s/"//g ;'s/'$//g"; s/^'//g"

This is much more efficient. Just make sure the semicolon separators are also protected from the shell by quotes, as I've shown.

Also, in s/'$//g the g is redundant, since more than one match is impossible. So it's equivalent to s/'$//. Same goes with s/^'// ; no line can have more than one beginning.

Bruce B

#207 Post by Bruce B »

PupGeek wrote:Turf, traditionally speaking, the variable "$1" handles
a parameter passed to the script at runtime. For example, if you write a
script that will execute a command or series of commands on a file, there
has to be a variable within the script that the filename is passed to.
No so. In many, many cases conditions are static. And there may be no
need for internal or external variables. See a couple examples.

fn: sloppy

Code: Select all

#!/bin/bash
cp /root/.jwm/jwmrc-personal-sloppy /root/.jwm/jwmrc-personal
jwm -restart
fn: click

Code: Select all

#!/bin/bash
cp /root/.jwm/jwmrc-personal-click /root/.jwm/jwmrc-personal
jwm -restart
The link you posted is leads me to a blank black page.
http://linuxreviews.org/beginner/Bash-B ... /x1458.htm

But from it, I found a great page on the site, which I think is the one you
wanted to reference.

http://linuxreviews.org/beginner/Bash-Beginners-Guide/

~

PupGeek
Posts: 353
Joined: Sun 06 Sep 2009, 11:30

#208 Post by PupGeek »

ah! I should have specified "when passing a filename (or other) argument to a script"

And yes, the bash beginners guide is what I wanted to reference. I was hoping, however, to reach that particular area of it is all.

Bruce B

#209 Post by Bruce B »

Shep,

Code: Select all

NEWNAME="`echo "$TRACK" |sed 's/[\(\)]//g'|sed 's/"//g'|sed "s/'$//g"|sed "s/^'//g"`" 
sed -e 's/[\(\)]//g' -e 's/"//g' -e "s/'$//g" -e "s/^'//g"

verses

sed s/[\(\)]//g'; s/"//g ;'s/'$//g"; s/^'//g"


I'll explain why I pipe sed strings, although this is not only about piping, I
learned something by the examples.

1) I wasn't born with any knowledge of sed, which means;

2) I had to read and learn from others, and;

3) Some Internet teachers, don't want to actually teach, maybe
intellectual narcissistic pigs, which probably means, I received bad
instructions and;

4) Things didn't work as explained, but I found piping works.

Thanks for the show how. I'll archive and study it.

~

Shep
Posts: 878
Joined: Sat 08 Nov 2008, 07:55
Location: Australia

#210 Post by Shep »

Keef wrote:A couple of simple questions...

I don't seem to have nl on my system (Wary 5) using Bash 3.
You can use cat -n filename instead.

Shep
Posts: 878
Joined: Sat 08 Nov 2008, 07:55
Location: Australia

#211 Post by Shep »

Keef wrote:A couple of simple questions...

How do we write a 'pipe'? eg how do I turn

Code: Select all

 sed 's/  */ /g' 
into 'onespace' ?
I think you are asking how you can create an executable file, or else a function, which can be used as a command named 'onespace'.
I've tried to look this up but got nowhere. I've found that mkfifo can make a named pipe, but how to put anything in the damn thing I can't discover.
Please put me out of my misery....
A named pipe is not something you need for this exercise. The pipe you would use is the one represented by | and used to feed the output of one command as input to another, as in:

echo "These words" | sed 's/^T/t/'

It's the use of a named pipe that I've come here to illustrate. :P
You need to arrange something to write data to one end of the named pipe, while a command reads data from the other end. In its barest form:

# mknod mypipe p
# echo "My Puppy Linux" >mypipe &
# sed 's/ Li.*//' mypipe
My Puppy


Using a named pipe is a handy construct, but probably not something you will need. The same result in this case can be achieved more easily using an ordinary pipe:

# echo My Puppy Linux | sed 's/ Li.*//'
My Puppy


While sed is a very handy stream editor, it is an external command so for brief use it is not as efficient as using a builtin. We can achieve the same result using the builtin command read to read a line from the pipe, followed by builtin Shell Parameter Expansion to strip off the trailing word:

# mknod mypipe p
# echo "My Puppy Linux" >mypipe &
# read xx <mypipe
# echo ${xx%% Linux*}
My Puppy

The one-liner example (above, in blue) can be converted to use the builtin read command, but to make it work as we want, two commands have to be grouped together. I prefer to use the very efficient curly brackets, though you will find most people use the familiar parentheses. They are actually very different, though that's not a discussion to get into right now. Here's the one-liner using builtins:

# echo My Puppy Linux | { read xx ; echo ${xx%% Linux*} ;}
My Puppy


Right at the start it is good programming practice to first delete any preexisting pipe named "mypipe" before trying to create a new empty one:

# if [ -p mypipe ]; then rm mypipe; fi

And to clean up, when finished with the pipe use rm mypipe to delete it..

The echo command seems to serve such a simple purpose, but actually it allows some quite complicated options. Sometimes, you may be puzzled by unexpected behaviour. It may surprise you to learn that linux has two, sometimes three, commands each named "echo". They have useful differences. One of the "echo"s is builtin, the others are external. Normally, operation defaults to the builtin command, but to leave no doubt that your code will use the builtin, you can precede each use with that keyword, e.g., builtin echo Hello. To force your code to use the external command, you use its full pathname, e.g., /bin/echo Hello. To see a list of all your shell builtins, type: enable .

HTH :D

This reply is a work in progress; I may come back and add to it.
Last edited by Shep on Wed 27 Apr 2011, 09:11, edited 10 times in total.

User avatar
01micko
Posts: 8741
Joined: Sat 11 Oct 2008, 13:39
Location: qld
Contact:

#212 Post by 01micko »

thanks shep :)

I have rewritten Bruce's script but I added the "\" (backslash) for human readability to the sed calls ala rerwin (our modem guru)

Code: Select all

#!/bin/bash

LIST=`ls "$1"|sed -e "s/'/foobar/g" \
              -e 's/ /_/g' \
              -e "s/$/'/" \
              -e "s/^/'/"` #get the list of filenames and filter out space, apostrophe, leading/trailing single quotes
cd $1
for TRACK in $LIST
 do
 echo $TRACK
 NEWNAME="`echo "$TRACK" |sed -e 's/[\(\)]//g' \
                          -e 's/"//g' \
                          -e "s/'$//" \
                          -e "s/^'//"`" #geany' #construct a new name, filter out braces, quotes
 OLDNAME="`echo $TRACK|sed -e "s/foobar/'/g" \
                            -e 's/_/ /g' \
                            -e "s/'$//" \
                            -e "s/^'//"`" #geany' #restore original filename
 FOOBAR=`echo $NEWNAME | grep foobar`
  if [ "$FOOBAR" != "" ];then NEWNAME=`echo $NEWNAME|sed 's/foobar//g'` #apostrophe work-around
  fi
 mv "$OLDNAME" $NEWNAME
 done
cd $HOME 
Cheers

EDIT: :oops: forgot to remove stray global calls (g for start and finish of lines), .. done!
Puppy Linux Blog - contact me for access

Bruce B

#213 Post by Bruce B »

01micko wrote:thanks shep :)

I have rewritten Bruce's script but I added the "" (backslash) for human readability to the sed calls ala rerwin (our modem guru)
Really nice job. Looks great. But, well, it's not my script.

~

User avatar
01micko
Posts: 8741
Joined: Sat 11 Oct 2008, 13:39
Location: qld
Contact:

#214 Post by 01micko »

Bruce B wrote: ..well, it's not my script.
yes it is.. I sold it to you for an IOU :wink:
Puppy Linux Blog - contact me for access

Bruce B

#215 Post by Bruce B »

How To Say "No to all"

A mild irritation from MS-DOS days. If set not to clobber files, MS-DOS
presents these options in multiple batch type operations: Yes, No, or
Yes To All
. Would it have been too much for Microsoft to give a No to
all
option?

A rhetorical question, obviously.

Today is - Linux.

Suppose our script has this command:

Code: Select all

mv $OLDNAME $NEWNAME
It would clobber any existing files which match $NEWNAME

Code: Select all

mv -i $OLDNAME $NEWNAME
Prompts the user for input, before clobbering. It does it one file at a time.

Code: Select all

false | mv -i $OLDNAME $NEWNAME
Grants my wish for a "No To All"

Code: Select all

false | mv -i $OLDNAME $NEWNAME 2> errors.txt
Grants my wish - plus a bonus! It gives a written list of all files that didn't
get clobbered.

~

SimpleWater
Posts: 94
Joined: Tue 19 Apr 2011, 11:53

#216 Post by SimpleWater »

As you can see, you have a blank page with the text "#!/bin/sh" as the first line. All shell scripts must begin with this line
Bruce B teaches with #!/bin/bash, care to elaborate a little more? Newbies need to know

Bruce B

#217 Post by Bruce B »

SimpleWater wrote:
As you can see, you have a blank page with the text "#!/bin/sh" as the first line. All shell scripts must begin with this line
Bruce B teaches with #!/bin/bash, care to elaborate a little more? Newbies need to know
Happy to. I'm here to serve, and learn.

The first line explicitly says what interpreter to use.

#!/bin/bash

This instructs Linux to use the bash interpreter in the /bin directory.

All my scripts are written in bash and are not tested against any other
interpreter.

There are variances, little nuances between otherwise compatible
interpreters. If I over looked some variance, it won't matter, because my
script says to explicitly use bash.

While on the subject, I wish to add, the first line is not requisite. If you
don't insert it, Linux will use the SHELL variable, which in the case of
Puppy is /bin/bash by default.

What is #!/bin/sh? The answer is, in Puppy it is Bash.

How do can you know?

cd /bin
ls -l sh


You discover sh doesn't exist, it is nothing other than a symlink to bash.

lrwxrwxrwx 1 root root 4 2011-01-04 03:37 sh -> bash


It should not make a bit of difference, in Puppy, if the fist line was
#!/bin/sh or #!/bin/bash

Except one is more direct. Why use a symlink to get to bash when I can
just go straight to bash?

~

SimpleWater
Posts: 94
Joined: Tue 19 Apr 2011, 11:53

#218 Post by SimpleWater »

ah that makes perfect sense, and i did test it myself, and yes it is only a symbolic link.
Except one is more direct. Why use a symlink to get to bash when I can
just go straight to bash?
Which is why i edited my beginner scripts to #!/bin/bash instead of the former, thanks for clearing things up, i'll be using the bash directly from now on.

edit: rox filer> right-click> new> script. Another reason i was doing it that way, was the default for creating a new script on right click, is #!/bin/sh

Bruce B

#219 Post by Bruce B »

SimpleWater,

I have an older version of Photoshop. It works great and does all I want.
The problem is there are some jpeg formats which were developed after
my old Photoshop. It will open these files, but when I save the changes, it
crashes. In order to work around this problem I wrote a script to convert
jpeg files to baseline format.

However the script (below) made a mess of my computer. But you can't
debug it and tell why or how, because the problem is not in the script.

Code: Select all

#!/bin/bash

# lossless conversion to baseline format
# removes extraneous data from file

TMPDIR=wrkdir

mkdir $TMPDIR
for i in *.jpg ; do
	jpegtran -copy none -outfile $TMPDIR/$i $i
	mv -v $TMPDIR/$i .
done
rmdir $TMPDIR
I keep my scripts in their own directories so they don't get mixed up with
anything else. I add the directories to the path as the first in the search
path.

This is normally not wise. But I have reasons for wanting to give my
scripts priority.

I intended to name the script baseline, because that's what it does, it
converts jpeg files to baseline format. We want to give files meaningful
names right?

But I named it basename. This would have been OK, if my scripts
were at the tail end of the search path.

Basename is the name of a utility which can be used by any number of
scripts. But my basename program doesn't give the basename, it messed
up other programs.

As it worked out, I debugged it.

The lessons here are three:

1) check if a file or function exists before giving your script a name

2) put your scripts in a path at the tail of the path statement, unless you
have good reason not to

3) even after you learned, you're still going to screw up.

Bruce

~

Bruce B

#220 Post by Bruce B »

SimpleWater wrote:edit: rox filer> right-click> new> script. Another reason i was doing it that way, was the default for creating a new script on right click, is #!/bin/sh
Puppy has its DNA. One of the things nice about having forum members
with lot of years on the forum, is, they remember.

A former significant contributor, GuestToo, contributed the script. The time
stamp says: Apr 27, 2005

I'm not sure on that date Bash was a default in Puppy.

~

Post Reply