shell求絕對值及一份不錯的shell資料

bulletming發表於2019-04-27
轉自:
將目前大家的方法稍微整理了一下,都寫成了函式:
abs () { echo ${1#-};}
abs () { echo $1 | tr -d -;}
abs () { echo $1 | awk '{print sqrt($1*$1)}';}
abs () { echo $1 | sed 's/^-//';}

也許以後大家遇到相同的問題,在不同的情況下能找到最適合的方法。
 
另外發現一個不錯的shell文件:
SHELL SCRIPTING 教學與心得



前言

底下的教學與心得分享是假設你已經有著基本的UNIX or Linux觀念
與技巧, 同時我們利用Linux預設提供的bash shell來操作, 例如 :
  • - login in
  • - "basic" commands (ls, mkdir..etc)
  • - how to move around the system(with cd command)
  • - 使用 vi
同時讀者可能也必須瞭解基本程式控制, 例如 : 變數定義, 流程控制 loop 等. 另外, shell script的技巧基本上是不分UNIX or Linux的, 所以學會 shell script是很吃香的喔! 作為一個系統管理者, 除了在系統管理上 需要熟悉那些工具, 檔案的位置等等外, shell script絕對是不可或缺的 能力. 同時, 也希望如果你覺得這個對你有幫助, 請給我一個email幫我加油 !


基本觀念與操作

建立第一個shell script
$ pwd
/home/xfish/bin
$ ls 
myscript.
myscript2
$ cat myscript.
ls -aF
$ myscript.
bash: ./myscript. Permission denied
$ chmod +x myscript.
$ myscript.
./       ../       myscript*     myscript2*

shell script其實就是一個很簡單的文字檔案,  檔案裡面有著
可以操作與控制相關動作與命令, 同時它必須具備能夠執行的能力,
在UNIX與Linux環境下, 也就是必須有 +x 的屬性 

上面是一個很簡單的shell script, 簡單地列出目前目錄下的檔案.

同時由於Linux/UNIX底下有多種shell的編譯器, 我們也可以在該script.
中的第一行來指定利用特定的編譯器執行, 例如我們可以指定 : 
#!/bin/bash 

使用echo命令

echo是用來顯示輸出的命令, 用幾個範例做個解釋, 後面的章節
將會看到常常利用echo來做些輸出的動作 : 

$ echo a     b      c
a b c
$ echo "a           b        c"
a           b        c
$ echo $0
-bash       (說明, 這個會依照你使用的shell不同而改變)

同時echo還能搭配以下特殊符號來控制特別輸出 : 

\a	alert, beeps the bell
\b	backspace character
\c	suppresses the new line
\f	formfeed character
\n	new-line
\r	return character
\t	tab character
\v	vertical tab character
\\	single backslash character


起始檔案 startup files

跟以往的DOS相同, 使用者登入後, 系統的shell在讀取完系統的
設定檔後(/etc/profile), 會自動讀取使用者shell, 
例如 : $HOME/.bashrc 或者 $HOME/.profile

因此, 若有一些需要使用者登入後就執行或者設定的動作, 可以在這
兩個檔案作設定的起始檔案

命令區隔
若有同時兩個命令要執行, 必須使用 ; 來區隔 

$ date who    (錯誤)
date: bad conversion
$ date ; who  (正確)
Tue Aug 25 15:38:24 2005
xfish		tty00		Aug 25 15:30
root		ttyp1		Aug 25 10:00
$ cd bin ; pwd
/home/xfish/bin
$ cd .. ; pwd
/home/xfish

Grouping 命令
利用( ) 來整合命令, 例如 : 
$ pwd
/home/xfish
$ ( cd /bin ; pwd )
/bin
$ pwd
/home/xfish
$ ls -l /usr/bin ; ls -l /usr/share | wc -l
這樣會列出 /usr/bin 與/usr/share 目錄下的檔案, 但是
事實上我們只要算算這總共有多少命令, 因此我們可以利用下面的命令取代 :
$ (ls -l /usr/bin ; ls -l /usr/share ) | wc -l
185
$


Shell Script. 概論

這一章, 我們會談到包括一些使用shell script的基本方法, 主要會提到 :
  • Aliases 別名
  • Here Documents 可以說是一種內嵌的檔案
  • Job Control 工作控制

Aliases 別名

直接在命令提示符號下 : alias 命令 , 你就可以看到目前定義的 alias.
而alias就是常常用來把一大串命令縮寫的常用技巧.

比如說我喜歡ls的時候加上ls -F選項, 同時我希望打ls時候預設就帶入 -F
引數, 就可以利用 : 

$ alias ls='ls -F' 來達到你的目的
多看幾個例子 :
$ alias lf="/usr/bin/ls -CaF"  
$ alias rm=echo  
$ rm /tmp/junk 
/tmp/junk 

Here Documents

一種內嵌於shell script的技巧, 直接用例子說明 :

$ cat phone 
#!/bin/bash 
grep -i $1 << END  
A Company 404 123 5888
Rosebery Corp. 314 713 7639
Chedworth.com 212 987 6543
Atlanta.Net 770 111 2222
END

$ phone atla 
Atlanta.Net 770 111 2222    (這是輸出結果) 

另外, END 那個只是一個識別, 也就是說, 你可以用其他字元代替,
不過還是建議用單字或者字串做為識別, 而不要用符號

Job Control 工作控制

工作流程控制, 大概有幾個命令一定得先了解, 包括了 :

  • jobs [-lp] [job]
  • bg [job..]
  • fg [job..]
  • kill [-signal] job..
  • wait [job..]
同時上述的 [job] 可以用這樣的方式來表示 :
  • %number : refer to job by number
  • %string : job whose name begins with string
  • %?string : job whose name contains string
  • %+ or %% : current job
  • %- : previous job

所以希望參考完這個例子, 你就能對Job Control工作流程控制有些瞭解.

$ stty susp ^Z                      (確保 susp 是 ^Z (Ctrl + Z)) 
$ stty tostop 
$ ls -R /                           (這個命令可以讓系統跑很久, 按下 Ctrl + Z) 
^Z
[1]+ Stopped  ls -R /               (系統提示這個編號 1 的 job 被 stopped)
$ bg                                (執行 bg 命令, 把編號 1 丟到後端去跑)
[1]+ ls -R / &   
$ (sleep 36 ; echo Hello World) &   (執行第二道命令)
[2] 656                             (系統給編號 2 的命令)
[1]+ Stopped  ls -R / 
$ jobs                              (利用 jobs 命令 檢視所有後端工作)
[1]- Stopped ls -R / 
[2]+ Stopped (sleep 36;echo Hello World) 
$ kill %-                           (刪除前一編號的工作)      
[1]- Stopped ls -R / 
$ jobs                              (利用 jobs 命令 再次檢視所有後端工作)
[1]- Terminated ls -R / 
[2]+ Stopped (sleep 36; echo Hello World) 
$ fg %2                             (把編號 2 命令帶到 前端 )
(sleep 36; echo Hello World) 
Hello World                         (列印出 結果 並結束 )
$ 


變數定義

變數在shell script裡面扮演很重要的腳色, 會寫程式的都應該知道變數的定義, 其重要性, 也不用我多說, 我們這邊會包括以下幾部分 :
  • 變數的定義
  • 保留/系統預設的變數
變數的定義

還是直接用例子來說明, 最容易瞭解. 底下這些都是變數定義的方式(在/bin/bash或者/bin/sh環境下) :

$ name=Derek 
$ fullname='Derek Super' 
$ completename="Derek Super Man" 
$ echo my name is $name, $fullname, $completename 
my name is Derek, Derek Super, Derek Super Man 
$ 
$ question='What is "sheckle"' 
$ echo my question is $question 
my question is What is "sheckle" 
$ 

保留/系統預設的變數

系統預設已經有相當多的變數定義了, 因此在你的shell script裡面要去避免這些變數. 以下就是一些預設的系統變數.

BASH_ENV		absolute path of startup file
CDPATH			directories searched by cd
FCEDIT			absolute path of history editor
HISTCMD			the history number of the current command
HISFILE			absolute path of history file
HISTSIZE		number of remembered commands
HOME			login directory
IFS			token delimiters
LINENO			current line number in shell script.
LINES			terminal height
MAIL			absolute path of mailbox
MAILCHECK		number of seconds to check mail
OLDPWD			absolute path of previous directory
OPTARG			option set by getopt
OPTIND			option's ordinal position set by getopt
OSTYPE			the OS on which bash is executing
PATH			command search path
PPID			process ID of parent
PS1			primary prompt
PS2			secondary prompt
PWD			absolute path of current directory
RANDOM			random integer
REPLY			default variable for read
SECONDS			number of seconds since shell started
SHELL			absolute pathname of preferred shell
TMOUT			seconds to log out after lack of use
UID			user ID of the current user
$			process ID of current shell
?			exit status of most recent statement

還有一些常用的 符號變數, 分列如下 :

$#	引數的數目
$*	代表所有引數
$?	Exit status of previous command
$$	PID of this shell's process
$!	PID of the most recently started backgroup job


shell的判斷式

從這邊開始, 我們將開始進入一些shell script的判斷, 就是說
我們會利用到if-then-else的一些判斷, 來讓shell script具備判斷能力.

討論的主題預計包括 : 
  • If-Then-Else 敘述
  • Testing 判斷式
  • Case 敘述
If-Then-Else 先用一個簡單的例子, 來敘述If-Then-Else敘述. $ cat myscript5 #!/bin/sh if grep $1 /etc/passwd then echo "FOUND !!!!" fi $ myscript5 xfish xfish:x:200:200:Xfish:/home/xfish:/bin/bash FOUND !!!! $ myscript5 ABCdefga $ 說明 : myscript5 將會對/etc/passwd檔案 進行尋找, 若找到引數所指定的字 串, 就印出 FOUND !!!! 因為我們/etc/passwd有xfish這個帳號, 因此 第一個命令會找到 該字串, 並印出 FOUND !!!! 所以 由於我們/etc/passwd並沒有ABCdefga字串, 因此直接結束程式. 沒有印出任何訊息. 但是如果我們只想要 在有找到我指定的字串時, 簡單的輸出 FOUND !!!! 不要把 grep 命令的 output 印出來, 同時加上一些變數, 讓輸出 更多樣化, 我們可以把 myscript5 再修改成 : $ cat myscript5 #!/bin/sh if grep $1 /etc/passwd > /dev/null then echo "found $1" else echo "didn't find $1" fi $ myscript5 xfish found xfish $ myscript5 ABAaa didn't find ABAaa 那如果多重式的if-then-else要怎麼寫呢? 我們再以剛剛的例子來變化. $ cat myscript6 #!/bin/sh if grep $1 /etc/passwd > /dev/null then echo "found $1 in /etc/passwd" elif grep $1 /etc/greoup > /dev/null then echo "found $1 in /etc/group" else echo "didn't find $1" fi $ myscript6 other found other in /etc/group $ myscript6 AabcdB didn't find AabcdB 關於if-then-else的寫法, 我自己通常的習慣, 都會將 then 往上提, 這樣的寫法 可以讓程式行數少一點, 比較美觀一點. 參考看看. $ cat myscript6 #!/bin/sh if grep $1 /etc/passwd > /dev/null; then echo "found $1 in /etc/passwd" elif grep $1 /etc/group > /dev/null; then echo "found $1 in /etc/group" else echo "didn't find $1" fi Testing 判斷式

Testing 判斷式通常與if-then-else一起互用, 以便發揮其效果.
先從一個最簡單的例子看起 :

$ cat myscript7
#!/bin/sh
if [ -f /etc/passwd ]; then
   echo "/etc/passwd is a file"
else
   echo "PANIC : /etc/passwd is not a file!!"
fi

先說明一下 [ -f /etc/passwd ] 這個最重要的部份, [ ] 裡面
就是判斷式, 而這裡是判斷 /etc/passwd 是不是一個 file ?
由結果來看看是執行 then 下面的敘述, 或者執行 else 的敘述.

同時很重要一點是 底下這些敘述都是不正確的敘述, 會有錯誤產生喔 :
[-f /etc/passwd ]		[-f 連在一起 
[ -f /etc/passwd]		passwd] 連在一起
[ -f/etc/passwd ]		-f與/etc/passwd連在一起

所以利用 [ ] 我們可以做出以下這些判斷, 這些也都很常用喔 !!

test					true if ....

[ string1 = string2 ] string1 and string2 are equal [ string1 != string2 ] string1 and string2 are not equal [ string1 \< string2 ] string1 is lexically less than string2 (e.g. 'a' is less than 'b') [ string1 \> string2 ] string1 is lexically greater than string2 (e.g. 'b' is greater than 'a') [ -z string ] string is zero (e.g. a empty string) [ -n string ] string is nonzero (e.g. a VAR string) [ -e file ] file exists [ -f file ] file is a file [ -d file ] file is a directory [ -c file ] file is a character device [ -b file ] file is a block device [ -p file ] file is a named pipe [ -s file ] file is not empty [ -k file ] file's sticky bit is set [ -S file ] file is a socket [ -L file ] file is a symbolic link [ -r file ] file is readable by user [ -w file ] file is writeable by user [ -x file ] file is executeable by user [ -O file ] file is owner by user [ -G file ] file is group owned by a greoup [ -u file ] file has its set user ID bit set [ -g file ] file has its group user ID bit set [ file1 -nt file2 ] file1 is newer than file2 [ file1 -ot file2 ] file1 is older than file2 [ file -ef file2 ] file1 is another name for file2 [ n1 -eq n2 ] true if integer n1 = integer n2 [ n1 -ne n2 ] true if integer n1 <> n2 [ n1 -gt n2 ] true if n1 > n2 [ n1 -ge n2 ] true if n1 >= n2 [ n1 -lt n2 ] true if n1 < n2 [ n1 -le n2 ] true if n1 <= n2
case 敘述

case 敘述與 if-then-else 效用類似, 請參考下述例子 :

$ cat myscript9
#!/bin/sh
case $1 in
  [a-z]*)	echo "starts with lower case"
		;;
  [A-Z]*)	echo "starts with upper case"
		;;
  ?*)		echo "starts with something else"
		;;
  *)		echo "doesn't start"
esac

case敘述是由 case 開頭與 esac (case的到寫) 組合而成, 而每一個
判斷之後的兩個分號 (;;) 就是代表 break 的功能, 也就是說遇到 ;;
就會跳出整個 case 判斷式.

而上述的範例能夠判斷script所附帶的引數, 若引數為小寫字母開頭,
則印出 "starts with lower case", 反之若是大寫字母開頭, 則印出
"starts with uppper case".

再用一個簡單的例子說明 :

$ cat myscript10
#!/bin/sh
case $1 in
  0)	echo zero ;;
  1)	echo one ;;
  2)	echo two ;;
  3)	echo three ;;
  4)	echo four ;;
  5)	echo five ;;
  6)	echo six ;;
  7)	echo seven ;;
  8)	echo eight ;;
  9)	echo night ;;
  *)	echo $1 ;;
esac


迴圈 for while loop
迴圈的宣告基本上所有的語言都是類似, 也就是都靠
while, until 或者 for 這兩個方法來做迴圈.

while迴圈

$ cat myloop1
#!/bin/sh
read -r filename
while [ ! -f $filename ]; do
  echo "--&gt $filename does not exist"
  read -r filename
done
echo "$filename name OK"

$ myloop1
/etc/password
--&gt /etc/password does not exist
/etc/groups
--&gt /etc/groups does not exist
/etc/group
/etc/group name OK
$

說明 :
執行後, 會等待使用者輸入一個檔名, 接著會檢查
該輸入字串是否真為一個檔案 (利用 -f 判斷), 若是
則跳出 while loop 並且印出 "檔名 name OK"
反之, 則印出 "--&gt 檔名 does not exist" , 並繼續
要求使用者輸入.

until 迴圈

同 while , until 的功用與語法很類似, 請參考

$ cat myloop2
#!/bin/sh
until who | grep dino > /dev/null; do
  sleep 10
done
echo "dino has logged in"

$ myloop2 &
[2] 9281
$
$
$ dino has logged in

我們再用一個例子來讓迴圈多點變化 :

$ cat myloop3
#!/bin/sh
i=1
while [ $i != 7 ]; do
  echo $i
  i=`expr $i + 1`
done

$ myloop3
1
2
3
4
5
6
$

這個範例 我想就不用怎麼說明瞭, 不過利用這個範例
跟大家分享一下, 我常常用來做倒數計時的一個迴圈 :

#!/bin/sh
interval=10
total=1
while [ $total -le 5 ]; do
  i=1
  while [ $i -le $interval ]; do
    echo -e "$total / $i / $interval \r\c"
    sleep 1
    i=`expr $i + 1`
  done
  total=`expr $total + 1`
done

請大家試試 就可以知道 這大概是什麼用的, 不過有時候
echo 的語法 似乎在各種不同的 shell (e.g. /bin/sh, /bin/bash)
之間 並不完全相同 這可能要花一點點時間去確認的.

for 迴圈

繼續談到 for 迴圈, for 迴圈通常使用在比較有明確範圍的情況.
例如 :

$ cat myloop4
#!/bin/sh
for foo in *; do
  if [ -f $foo ]; then
     echo "$foo is a file"
  elif [ -d $foo ]; then
     echo "$foo is a directory"
  else
     echo "$foo is not a file, nor directory"
  fi
done

$ myloop4
snake.txt is a file
tab.txt is a file
tinrc.txt is a file
tmp is a directory
tmp1 is a file


sed 字串編輯器
這部份要先偷懶一下, 因為之前已經寫過一些 sed 的簡易說明,
這裡直接把它拿過來, 同時加上一些補充.

  • 將 filename 檔案內的 Giga 字串取代成 GigaRama sed s/Giga/GigaRama/ filename 但是我們這樣只能做 stdout , 若是要把 file1 裡面的 Giga 都取代成 GigaRama 的話, 我們可以這樣做 : cat file1 | sed s/Giga/GigaRama/ > file2
  • 將 filename 檔案內的 xfish 字串那一行刪除 sed /xfish/d filename 同樣, 若是要將修改過的檔案變成一個新檔案, 可以這樣做 : cat file1 | sed /xfish/d > file2
  • 指定哪一行,將之刪除 sed '4d' filename 這裡是指定第四行 刪除
  • 將filename的第一行到第幾行,將之刪除 sed '1,4d' filename 這裡是指定第一行到第四行 刪除
  • 將filename的第一行到第五行印出 sed -n 1,5p filename
  • 將 file 檔案內的出現 xfish 字串的那一行單獨寫到 file2 內 sed -n '/xfish/w file2' file
  • 萬用字元的使用,將 file 檔案內的 xfis? 哪一行寫到 file2 內 sed '/xfis./w file2' file
  • 萬用字串的使用,將 file 檔案內的 xfis* 哪一行寫到 file2 內 sed '/xfis*/w file2' file
  • 選定字元的使用,將 file 檔案內的 xfis[abcd] 哪一行寫到 file2 內 sed '/xfis[abcd]/w file2' file
  • 特別符號的取消,利用 / sed s/\/\/ file
  • 一行的起頭的取代,將 file 檔案的每一行起頭都加上 Hi.. sed s/^/Hi.. / file
  • 一行的結尾的取代,將 file 檔案的每一行結尾都加上 Hi.. sed s/$/Hi.. / file
  • 多重條件的指定,利用 -e 選項 sed -e 's/Giga/GigaRama/' -e 's/^/Hi../' file

awk 結構化的資料處理工具
(未完成)

範例 (未完成)
這些部分都是我自己寫的例子, 可能不完全正確, 或者說
還有很多可以改進的地方, 如果有任何建議, 請給我email.

還有一些部份是直接從網路上收集的, 挑幾個作一些參考.

另外, 若你有什麼好的script, 願意一起放進來, 我都很歡迎喔.:)
  • 把目錄下大寫的檔案名稱 修改成小寫的檔案名稱 #!/bin/sh for file in [A-Z]*; do echo "processing $file" mv $file `echo $file | tr '[A-Z]' '[a-z]'` done
  • 修改檔名, 把 *.htm 修改成 *.html 這個在DOS下可能用一個 rename 就可以解決了, 但是很抱歉 UNIX/Linux上面可不行. 所以 shell script. 就可以幫上忙了. #!/bin/sh for i in *.htm ; do echo "processing $i" mv $i `basename $i .htm`.html done
  • 輸入 mycmd f1 f2 f3 f4 .. fn 把每個 f* 複製成 f*.bak 利用 while 與 shift 的技巧 : #!/bin/sh while [ $# -gt 0 ]; do # 註解 把 echo 拿掉 就真的變成命令了.. # 這裡只有 echo 出來 並沒真正執行 echo "cp $1 $1.bak" shift done
  • 系統登入後 提示選單 選擇連上 PTT BBS (2007/03/22新增) 這是懶人的做法, 就是登入login系統後, 出現一個選單, 選單內 讓你能夠選擇連上 PTT BBS , 並且自動登入 PTT BBS , 這個有利用 expect , 也就是hacker喜歡用的一個工具 , 直接看例子吧 : 檔案 1 : 命名 bbs.sh #!/bin/bash while [ 1 ]; do clear echo "===================================================" echo " (1) PTT BBS " #註解:我只給一個選項 for test echo " (q) QUIT " #註解:要更多 請自己加 echo " (0) 離開選單 " #註解:要更多 請自己加 echo "===================================================" echo -n "your choice? ==> " read ans case $ans in 1) ptt.expect #註解:所以你還必須有個 ptt.expect 檔案 ;; q) kill -9 $MY_PID #註解:這個必須在你的 .login 中註明 ;; 0) exit ;; *) echo "Please choose the number from the list" ;; esac done 檔案結束 檔案 2 : 命名 ptt.expect #系統必須安裝expect套件喔 #!/usr/local/bin/expect -- #註解:路徑請自行修改喔 set host 140.112.90.72 #註解:這裡是ptt.cc的 IP , 用ptt.cc也OK spawn telnet $host expect "入代號" send "superman\r" #註解:把你的帳號名稱放這裡 expect "輸入您的密" send "supermanisgoodboy\r" #註解:把你的密碼放這裡 send "\r" interact 檔案結束 檔案 3 : $HOME/.login or $HOME/.bashrc or $HOME/.tcshrc 依照你自己設定 看看你一進入系統會讀取哪個檔案 我只有用兩行 說明 當然你可能還有自己的變數 or 設定 export MY_PID $$ #註解:這個是給 bbs.sh 用的 bbs.sh #啟動剛剛的 bbs.sh 路徑自己寫. 檔案結束


版權說明

本份學習講義, 是根據Caldera System出版的LINUX SHELL SCRIPTING所
整理而成的檔案, 這份講義, 內容除了本身所提供外, 很多範例與說明都是
由該本書擷取出來, 因此我覺得我只是整理出來. 雖然有加上我自己的一些
經驗與自己寫的一些 script, 但是為了尊重原本製作的公司與個人. 
所以我只擁有檔案整理權. 

許景超 - Last update : 2005/11/11 - 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/82392/viewspace-175136/,如需轉載,請註明出處,否則將追究法律責任。

相關文章