Aqui temos um livro livre e completo sobre Shell

Os sedentos do "saber livre" são muito benvindos.

Você está aqui: TWikiBar > TWikiBarTalk007
Controles: EDITAR ANEXAR MAIS MAIS ALTERACOES IMPRIMIR - Última Atualização: [18 Mar 2014 - V.10]

Pub Talk Part VII

This is a very new translation from Portuguese to English. Please contribute to the development of this site indicating errors and and/or suggesting corrections and materials for this person.

     - What's up dude! It was very difficult to do the little script I asked you?

     - Yeah, I really had to think hard in front of the black screen but I think I succeeded! Well, at least in the tests I did the work thing, but you always have to look for problems!

     - Not so, in shell programming is very easy, what counts are the tips and tricks that are not trivial. The fixes that I do, is just to show them. But we order two beers while I have a look in your script.

     - Hey waiter, brings two. Don't forget that one is no foam.

$ cat restore #!/bin/bash # # Restore deleted files with erreeme #

if [ $# -eq 0 ] then echo "Uso: $0 <File Name to Be Restored>" exit 1 fi # Picks up the original directory name in the last line Dir=`tail -1 /tmp/$LOGNAME/$1` # O grep -v deletes last line and creates the # file with directory and original names grep -v $Dir /tmp/$LOGNAME/$1 > $Dir/$1 # Remove file that already was to be eliminated rm /tmp/$LOGNAME/$1

     - Wait, let me get this straight. First you put the Dir variable the last line of the file whose name is formed by /tmp/name operator ($LOGNAME)/parameter passed to the file name to be restored ($1). Then the grep -v you rode deletes the row that was the name of the directory, that is, always the last and sends the rest of the file, so the file would already clean, to the original directory and then removes the file from the "trash"; S E N S A T IO N A L! Flawless! Zero error! See? You're already picking up the tricks of the shell!

     - So here we go, what you'll talk about today?

     - It'm seeing that the pet Shell caught you. That's nice, but let's see how you can (and should) read data and format screens and first let us understand a command that gives you all the tools for you to format your data entry screen.

The tput command

The largest use of this command is to position the cursor on the screen, but it is also very used to delete data from the screen, to know the number of rows and columns in order to correctly position a field, deleting a field whose criticism detected as wrong. Anyway, almost all the formatting of the screen is taken by this command.

A few attributes of the command tput can not possibly work if the terminal model defined by the variable $TERM does not have this corporate facility.

The table below presents the key attributes of the command executed and the effects on the terminal, but you see there are many more than that, get this:

$ tput it 8

In this example I received the initial size of the <TAB> ( Initial T ab), but tell me: what is this? If you want to know everything about the tput command (and looks is something that never ends), see: http://www.cs.utah.edu/dept/old/texinfo/tput/tput.html#SEC4.

Top tput command options
setf   Changes the font color (set foreground)
tput Options   Effect
cup lin col   CUrsor Position - Positions the cursor on the line lin e col column. The origin is zero
home   Same as tput cup 0 0, that is, places the cursor in the upper left vertex of the screen
bold   Place the screen in mode of emphasis
rev   Place the screen in reverse video mode
smso   Same as above
smul   From this instruction, the typed characters appear underlined on the screen
blink   Typed characters appear flashing (not all terminal definitions accept this)
invis   From this point on, anything you type will appear in the video (Note: Unsure to read senhas)
sgr0   After using one of the above, use this to restore the screen to its normal mode
reset   Clears the terminal and restore your settings according to terminfo, in other words, the terminal returns to the standard set by the variable =$TERM  
lines   Returns the number of lines the screen at the time of instruction
cols   Returns the number of columns of the screen at the time of instruction
el   Erase Line - Deletes the line from the cursor position
ed   Erase Display - Clears the screen from cursor position
il n   Insert Lines - Inserts n rows from the cursor position
dl n   Delete Lines - Remove n rows from the cursor position
ech n   Erase CHaracters - Delete n characters from the cursor position
sc   Save Cursor position - Save cursor position
rc   Restore Cursor position - Place the cursor in the position marked by the last sc
flash   Makes a very fast reverse video. Ideal for calling attention to errors or interesting occurrence
smcup   Capture a current picture of the screen and saves it for later retrieval
rmcup   Reinstates on the screen the picture captured with the command tput smcup
setb   Changes the background color (set background)

Let's do a silly program to show some attributes of this command. It is the famous and celebrated Hello World, only this phrase is written on the screen center and reverse video and after that, the cursor returns to the position it was in before writing this sentence so creative. see:

$ cat alo.sh #!/bin/bash # Silly script to test # the tput command (version 1)

Columns=`tput cols` # Saving amount columns Rows=`tput lines` # Saving amount rows Row=$((Rows / 2)) # What is the middle line of the screen? Column=$(((Columns - 9) / 2)) # Centring screen message tput sc # Saving position of the cursor tput cup $Row $Column # Positioning to write tput rev # Reverse Video echo Hello World tput sgr0 # Restore video to normal tput rc # Restore the original cursor position

As the program is already commented all, I think the only explanation would be needed for the row that is created the variable Column and there is the strange number 9, but he is the size of the string that I want to write (Hello World).

Thus this program could only focus chains of 9 characters, but see it:

$ var=Talk $ echo ${#var} 4 $ var="Pub Talk" $ echo ${#var} 16

Ahhh, improved! So now we know that building = $ {# variable} = returns the number of characters = variable =. Therefore, we will optimize our program for him to write in reverse video in the center of the screen the string passed as a parameter and then the cursor to the position it was before running the script.

$ cat alo.sh #!/bin/bash # Silly script to test # the tput command (version 2)

Columns=`tput cols` # Saving amount columns Rows=`tput lines` # Saving amount rows Row=$((Rows / 2)) # What is the middle line of the screen? Column=$(((Columns - ${#1}) / 2)) #Centring screen message tput sc # Saving position of the cursor tput cup $Row $Column # Positioning to write tput rev # Reverse Video echo $1 tput sgr0 # Restore video to normal tput rc # Restore the original cursor position

This script is the same as before, except that we change the fixed value of the previous version (9), for ${#1}, where it's 1 is $1 that is, this construction returns the size of the first parameter passed to the program. If the parameter that I want to pass contains blanks, would have put it all in quotes, if not the $1 would be only the first piece. To avoid this trouble, just replace $1 for $*, which as we know is the set of all parameters. So that line would look like this:

    Column=$(((Columns - ${#*}) / 2))  #   Centring screen message

and the row echo $1 would be echo $*. But don't forget when running, passing as a parameter, the phrase you want to center.

Another example, all commented that uses a lot of artifice of the command tput is what we'll see. It serves mainly to show how to save a screen for later retrieval. Finally, a photography class screens :), check out:

$ cat screen_saver seq 2000 | xargs # Dirtying the screen Lin=$(($(tput lines) / 2)) # Calculating row and central column of the screen Col=$(($(tput cols) / 2)) tput cup $Lin; tput el; tput setf 1 # Positioning, erasing the centerline and coloring echo "In 10 seconds this screen will be captured and erase" tput civis # Invisible Cursor to improve presentation for ((i=10; i!=0; i--)) { tput cup $Lin $Col; tput el # Positions the center of the screen and clears the previous number echo $i sleep 1 } tput smcup # Taking a picture of the screen clear # I could have used tput reset tput cup $Lin echo "In 10 seconds the initial screen will be recovered" for ((i=10; i>0; i--)) { tput cup $Lin $Col; tput el # Positioned in the center of the screen echo $i sleep 1 } tput rmcup # Restored photo tput cvvis;tput setf 9 # Restored the cursor and color

So you can see all the color combinations of font and background (tput setf and tput setb), run the following script:

$ cat colors1.sh #!/bin/bash for ((b=0; b<=7; b++)) { tput setb 9; tput setf 9; echo -n "|" for ((f=0; f<=7; f++)) { tput setb $b; tput setf $f; echo -n " b=$b f=$f " tput setb 9; tput setf 9; echo -n "|" } echo } tput setb 9; tput setf 9

I said before that these were all combinations, but if tput bold is active, you will have 7 more font colors.

If you were out of time and wished not run this script to see the colors, their codes are as follows:

Terminal colors
9   Back to default color  
  Symbol     Color
0   Black  
1   Blue  
2   Green  
3   Cyan  
4   Red  
5   Magenta  
6   Yellow  
7   White  

And now we can read the screen data

Well from now on we will learn all about reading, just can not teach reading letters and whelks because if I knew, I would be rich, a pub London drinking scotch and not those in a pub drinking beer. But let's move on.

The last time we met here, I gave an introduction to the read command. To start a more detailed analysis check this:

$ read var1 var2 var3 Pub Talk $ echo $var1 Talk $ echo $var2 de $ echo $var3 Pub $ read var1 var2 Pub Talk $ echo $var1 Talk $ echo $var2 Pub

As you saw, the read receives a separate list for blanks and places each item on this list in a variable. If the number of variables is less than the number of items, the last variable is assigned the remainder.

I said separated list by whitespace? Now that you know all about $IFS (Inter Field Separator) that I introduced you when we talked for command, will still believe that? Let's test the direct prompt:

$ oIFS="$IFS" $ IFS=: $ read var1 var2 var3 Pub Talk $ echo $var1 Pub Talk $ echo $var2

$ echo $var3

$ read var1 var2 var3 Pub:Talk $ echo $var1 Talk $ echo $var2 of $ echo $var3 Pub $ IFS="$oIFS"

As you saw, was wrong! The read read a list, like for, separated by the characters of the variable $IFS. Then see how it can make your life easier:

$ grep julio /etc/passwd julio:x:500:544:Julio C. Neves - 7070:/home/julio:/bin/bash $ oIFS="$IFS" # Saving IFS $ IFS=: $ grep julio /etc/passwd | read lname trash uid gid coment home shell $ echo -e "$lname\n$uid\n$gid\n$coment\n$home\n$shell" julio 500 544 Julio C. Neves - 7070 /home/julio /bin/bash $ IFS="$oIFS" # Restoring IFS

As you saw, the output grep was redirected to the read command that read all fields at once. The and of the echo was used for the \n be understood as a line jump (new line), and not as a literal.

Under the Bash there are several options of read that serve to make your life easier. See the following table:

Options of the read command in Bash
  -s     What is being typed does not appear on screen  
  Option     Action
  -p prompt     Writes prompt before doing the reading  
  -n num     Reads up num characters  
  -t seg     Wait sec seconds for the reading to be completed  

And now straight to short examples to demonstrate these options.

To read a "Matriculation" field:

$ echo -n "Matriculation: "; read Mat # -n not jumping line Matriculation: 12345 $ echo $Mat 12345

Or simplifying with the option -p:

$ read -p "Matriculation: " Mat Matriculation: 12345 $ echo $Mat 12345

To read a certain number of characters:

$ read -n5 -p"CEP: " Num ; read -n3 -p- Compl CEP: 12345-678$ $ echo $Num 12345 $ echo $Compl 678

In this example did two read: one for the first part of the CEP and another for its complement, thereby formatting data entry. The dollar sign ($) after the last digit entered, is because the read have not new-line implied by default as is echo.

To read that up to a certain time runs out (known as time out):

$ read -t2 -p "Enter your full name: " Nom || echo 'So eazy!' Enter your full name: JSo eazy! $ echo $Nom


Obviously this was a joke, because it only had 3 seconds to enter my full name and only gave me time to type a J (one pasted on So), but it served to show two things:

  1. The command after the pair of vertical bars (||) (or logical, remember?) will run if the typing was not completed in the stipulated time;
  2. The Nom variable remained empty. It will be valued only when <ENTER> is entered.

To read a data without being displayed on the screen:

$ read -sp "Password : " Password: $ echo $REPLY secret :)

I took advantage of an error to show a trick. When I wrote the first line, I forgot to put the name of the variable that would receive the password, and only noticed when I was going to list their value. Fortunately the $REPLY Bash variable have the last string read and it took me not to miss the trip. Test yourself what I just did.

But the example I gave was to show that the option -s prevents what is typed to go to the screen. As in the previous example, the lack of the new-line has made the prompt of command ($) remained on the same line.

Well, now that we see how to read the screen reads file data.

Let's read files?

As I have told you, and you must remember, the while tests a command and executes a block of statements while this command is successful. When you are reading a file that gives you permission to read the read will only be unsuccessful when it reaches the EOF (end of file), in this way we can read a file in two ways:

1 - Redirecting input from file for the block while so:

    while read Row
        echo $Row
    done < file

2 - Redirecting output of a cat for while, as follows:

    cat file
    while read Row 
        echo $Row

Each process has its advantages and disadvantages:

Advantages of the first case:

* It's faster; * You do not need a subshell to watch it;

Disadvantage of the process:

* In a large block of instructions, the redirection is not very visible which sometimes impairs visualization of the code;

Advantage of the second method:

* As the name of the file is before of while is easier to visualize the code.

Disadvantages of the second case:

* The Pipe (|) calls a subshell to interpret it, making the process slower, heavier and sometimes problematic (see example below).

To illustrate what has been said, check out these examples:

$ cat readpipe.sh #!/bin/bash # readpipe.sh # Example of read passing file for pipe.

Last="(empty)" cat $0 | # Passing the script file ($0) for while while read Row do Last="$Row" echo "-$Last-" done echo "Finished, Last=:$Last:"

Let's see it's execution:

$ readpipe.sh -#!/bin/bash- -# readpipe.sh- -# Example of read passing file for pipe.- -- -Last="(empty)"- -cat $0 | # Passing the script file ($0) for while- -while read Row- -do- -Last="$Row"- -echo "-$Last-"- -done- -echo "Finished, Last=:$Last:"- Finished, Last=:(empty):

As you saw, the script list all their own rows with a minus sign (-) before and other after each, and at the end displays the contents of the variable $Last. Note however that the contents of this variable remains (empty).

     - Huh! Does the variable was not updated?

     - It was, and this can be proven because the row echo "-$Last-" correctly list the rows.

     - So why did this happen?

     - Because as I said, the block of instructions redirected by pipe (|) runs on a subshell and there the variables are updated. When this subshell ends, updates of variables go to hell along with him. Note that I'll make a small change in him, passing the file for input redirection (<) and things will operate in perfect order:

$ cat redirread.sh #!/bin/bash # redirread.sh # Example of read passing file for pipe.

Last="(empty)" while read Row do Last="$Row" echo "-$Last-" done < $0 # Passing the script file ($0) for while echo "Finished, Last=:$Last:"

And see his perfect execution:

$ redirread.sh -#!/bin/bash- -# redirread.sh- -# Example of read passing file for pipe.- -- -Last="(empty)"- -while read Row- -do- -Last="$Row"- -echo "-$Last-"- -done < $0 # Passing the script file ($0) for while- -echo "Finished, Last=:$Last:"- Finished, Last=:echo "Finished, Last=:$Last:":

Well friends Shell Network, to finalize the read command I just need to talk about a small and important trick. I will show using a practical example. Suppose you want to display a file in the list and this list of ten records stop so that the operator could read the contents of the screen and it only came back to roll (scroll) after the operator enter any key. To not wasting paper (Linux Magazine), I'll make this list horizontally and my file (numbers) has 30 records only with sequential numbers. see:

$ seq 30 | xargs -i echo lin {} > numbers # Creating numbers file $ paste -sd':' numbers # This paste is to display content without being a #+ per row, using colons as separator lin 1:lin 2:lin 3:lin 4:lin 5:lin 6:lin 7:lin 8:lin 9:lin 10:lin 11:lin 12:lin 13:lin 14:lin 15:l in 16:lin 17:lin 18:lin 19:lin 20:lin 21:lin 22:lin 23:lin 24:lin 25:lin 26:lin 27:lin 28:lin 29: lin 30 $ cat 10porpag.sh #!/bin/bash # Test program to write # 10 lines and stop to read # Version 1

while read Num do let ContLin++ # Counting... echo -n "$Num " # -n not to jump row ((ContLin % 10)) > /dev/null || read done < numbers

In an attempt to make a generic program we created the variable $ContLin (because in real life, the records aren't only sequential numbers) and we stopped to read when the rest of the division by 10 were zero (sending the output to /dev/null so as not to appear on the screen, dirtying it). However, when I run gave the following problem:

$ 10porpag.sh lin 1 lin 2 lin 3 lin 4 lin 5 lin 6 lin 7 lin 8 lin 9 lin 10 lin 12 lin 13 lin 14 lin 15 lin 16 l in 17 lin 18 lin 19 lin 20 lin 21 lin 23 lin 24 lin 25 lin 26 lin 27 lin 28 lin 29 lin 30 $

Note that missed the row lin 11 and the list did not stop at read. Happened that all input of loop was redirected from numbers file and thus the reading was taken upon this file, thereby losing lin 11 (and also lin 22 or any other row multiple 11).

We will show how it should be for run correctly:

$ cat 10porpag.sh #!/bin/bash # Test program to write # 10 lines and stop to read # Version 2

while read Num do let ContLin++ # Counting... echo -n "$Num " # -n not to jump row ((ContLin % 10)) > /dev/null || read < /dev/tty done < numbers

Note that now the input of read was redirected by /dev/tty, which is nothing more than the current terminal, explaining this so that reading would be taken from the keyboard and not the numbers. It's good to note that this doesn't happen only when we use the input redirection, if we had used the redirect with pipe (|), the same would have occurred.

See now it's execution:

$ 10porpag.sh lin 1 lin 2 lin 3 lin 4 lin 5 lin 6 lin 7 lin 8 lin 9 lin 10 lin 11 lin 12 lin 13 lin 14 lin 15 lin 16 lin 17 lin 18 lin 19 lin 20 lin 21 lin 22 lin 23 lin 24 lin 25 lin 26 lin 27 lin 28 lin 29 lin 30

This is almost good but lacking a bit to become excellent. We will improve a little example for you to play and test (but before testing increases the number of records numbers or reduce the size of the screen, so there is breakage).

$ cat 10porpag.sh #!/bin/bash # Test program to write # 10 lines and stop to read # Version 3

clear while read Num do ((ContLin++)) # Counting... echo "$Num" ((ContLin % (`tput lines` - 3))) || { read -n1 -p"Tecle Algo " < /dev/tty # to read any character clear # clears the screen after reading } done < numbers

A substantial change in this example is related to the page break, since it is made for each quantity-of-rows-of-screen (tput lines) less (-) 3, that is, if the screen has 25 rows, will list 22 records, and will stop for reading. In the read command a change was also made, inserted -n1 to read only one character without being necessarily a <ENTER> and the option -p to give the message.

     - Well my friend, we finished for today because I think you're tired...

     - I'm not, you can continue...

     - If you are not I am... But since you're so excited about Shell, I'll leave you an exercise in learning programs for you to improve your CD Library that is quite simple. Rewrite your program that registers CDs to mount the whole screen with a single echo and then go positioning in front of each field to receive the values ​​that are typed by the operator.

Vou aproveitar também para mandar o meu jabá: diga para os amigos que quem estiver afim de fazer um curso porreta de programação em Shell que mande um e-mail para a nossa gerencia de treinamento para informar-se.

Any doubt or lack of companionship for a beer or even to speak ill of politicians just send an email to of me .


Licença Creative Commons - Atribuição e Não Comercial (CC) 2017 Pelos Frequentadores do Bar do Júlio Neves.
Todo o conteúdo desta página pode ser utilizado segundo os termos da Creative Commons License: Atribuição-UsoNãoComercial-PermanênciaDaLicença.