2012/08/26

Linux 的輸入與輸出及pipeline


標準的輸入、輸出與管線

標準的資料通道

Standard io.png
  • Linux有三個標準的資料通道,分別是:
  1. stdin 編號 0 --預設為鍵盤
  2. stdout 編號 1 --預設為windows終端機
  3. stderr 編號 2 --預設為windows終端機
  • 簡單的說明
[mtchang@server1 ~]$ ls -l Desktop fakefile
# STDERR 輸出
ls: fakefile: No such file or directory
# STDOUT 輸出
Desktop:
total 12
-rw-r--r-- 1 mtchang mtchang 4803 Jan  7  2007 gnome-terminal.desktop
-rw-rw-r-- 1 mtchang mtchang  260 Aug 14 19:54 Thunderbird.desktop

轉移輸出

  • Linux的轉移資料字元:
  1. < 轉向標準輸入
  2. > 轉向輸出,如果該目標(檔案)已存在會覆蓋。
  3. 1> stdout轉向輸出,如果該目標(檔案)已存在會覆蓋。
  4. >> 轉向輸出如果該目標(檔案)已存在會添加在檔案後面。
  5. 1>> stdout轉向輸出如果該目標(檔案)已存在會添加在檔案後面。
  6. 2> stderr轉向輸出,如果該目標(檔案)已存在會覆蓋。如不想覆蓋可使用2>>添加在檔案後面。
  7. 2>> stderr轉向輸出,如果該目標(檔案)已存在會添加在檔案後面。
  8. &> all(0,1,2)轉向輸出到目標(檔案)
  9. &1> stdout的輸出轉向到目標(檔案)
  10. &2> stderr的輸出轉向到目標(檔案)
  11. &1 代表stdout的輸出
  12. &2 代表stderr的輸出
  13. Linux的特殊裝置 /dev/null 黑洞,任何輸入到/dev/null的資料皆會消失不顯示於任何地方。
  • < 轉向標準輸入的範例,把ls的內容轉入給mail程式
[lcc09@localhost ~]$ ls > ls
[lcc09@localhost ~]$ cat ls
Desktop
ls
public_html
[lcc09@localhost ~]$ mail lcc09 < ls
[lcc09@localhost ~]$ mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/spool/mail/lcc09": 1 message 1 new
>N  1 lcc09@localhost.loca  Fri May 25 20:50  17/659  
& 1
Message 1:
From lcc09@localhost.localdomain  Fri May 25 20:50:09 2007
Date: Fri, 25 May 2007 20:50:09 +0800
From: lcc09 
To: lcc09@localhost.localdomain

Desktop
ls
public_html

& q
Saved 1 message in mbox
  • 一個帶有 stdout 及 stderr 輸出的指令
[lcc09@localhost ~]$ ls ~/ /rec
ls: /rec: 沒有此一檔案或目錄
/home/lcc09/:
Desktop  public_html
  • 將 stderr 轉到 /dev/null 所以看不到錯誤的訊息
[lcc09@localhost ~]$ ls ~/ /rec 2> /dev/null
/home/lcc09/:
Desktop  public_html
  • 將 stderr 接到 stdout ,並將 stdout 存到 list
  1. >list :stdout轉向輸出到 list 檔案
  2. 2>&1 :stderr轉向輸出到 &1(stdout)
  3. &參考的檔案描述,為標準輸出編號,&1代表stdout
Redirect stderr2stdout.png
[lcc09@localhost ~]$ ls ~/ /rec > list 2>&1
[lcc09@localhost ~]$ more list 
ls: /rec: 沒有此一檔案或目錄
/home/lcc09/:
Desktop
list
mbox
public_html
  • 使用echo以keyboard為輸入裝置,並在銀幕上輸入文字,最後轉存到檔案。
[lcc09@localhost ~]$ echo "hello tux
>
> how r u
> i okey??
> save " > greetings
  • 練習:寫一個 shell script 可以自動產生一個 index.html 檔案,內容為標準的 html 檔案結構,並且在 body 內放入今天的日期。
[student@server1 ~]$ ./html.sh 
[student@server1 ~]$ nano html.sh 
#!/bin/bash
echo "



$(date +%Y%m%d) 


 " > index.html
[student@server1 ~]$ ./html.sh 
[student@server1 ~]$ cat index.html 




20091209 


 

補充:mail 指令的使用

  • mail:送出和接收email
  1. 在命令列輸入 mail 即可進入 mail 程式內收發信件
  2. 若想寫信給他人,則在命令列輸入 mail 收信者帳號 ,即可發送信件給「收信者」
  3. 若想將既有的文字檔當成信件內容寄給他人,則在命令列輸入
mail 收信者帳號 -s 信件主題 < 文件檔名稱
即可將「文件檔名稱」發送給「收信者」
  • 在 mail 程式中
  1. 輸入 ? 查詢所有指令
  2. 輸入 h 列出所有信件
  3. 輸入 t 閱讀信件
  4. 輸入 n 閱讀下一封信件
  5. 輸入 d 刪除信件
  6. 輸入 u 救回刪除信件
  7. 輸入 R 回覆信件
  8. 輸入 m 收信者帳號 發送信件給「收信者」
  9. 輸入 q 離開 mail 程式,並將閱讀過的所有信件存至「mbox」檔案
  10. 輸入 x 離開 mail 程式
  • 寄信
[mtchang@mt test]$ mail mtchang
Subject: test
test
.
Cc: 
  • 看信
[mtchang@mt test]$ mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/spool/mail/mtchang": 1 message 1 new
>N  1 mtchang@localhost.lo  Tue Oct 14 17:11  16/644   "test"
& 1
Message 1:
From mtchang@localhost.localdomain  Tue Oct 14 17:11:35 2008
Date: Tue, 14 Oct 2008 17:11:35 +0800
From: mtchang 
To: mtchang@localhost.localdomain
Subject: test

test

& q
Saved 1 message in mbox


標準輸出到程式(Piping)

  • 管線指令 pipe(|)
Redirect pipes1.png
[lcc09@localhost ~]$ command1 | command2 
# 將command1的輸出導入到command2的輸入
  • less 把輸出限制在一個頁面上,並且可以做功能性的操作,將ls輸出導入到less的輸入。
ls -l /etc | less
  • mail 寄郵件,將內容為「test email」的內容以「test」的主旨寄給 student@localhost 使用者,此功能可以寄出到外界的信箱,但是大部分的主機都會將其列為垃圾信件。
echo "test email" | mail -s "test" student@localhost
  • lpr 列印,或指定列印到某台印表機
echo "test print" | lpr
echo "test print" | lpr -p printer_name
  • 把 man ls 的資訊交由 col -b 過濾控制碼,並將其轉輸出到 ls.txt 檔案
[root@server1 ~]# man ls | col -b > ls.txt

多目標轉移

  • tee 指令的搭配,tee指令是一個雙向指令,會將標準輸入輸出到兩個地方(檔案及標準輸出)
  1. $ command1 | tee filename
  • 常見用途:
  1. pipelines 命令除錯
  2. 想要看結果也想要作logging
  • 簡單範例
[lcc09@localhost ~]$ man tee | col -b > tee.txt
# 將man tee的結果導入到col -b的輸入,並將結果輸出到tee.txt
[lcc09@localhost ~]$ ls -l | tee output
# tee這個指令是一個雙向指令,會將標準輸入輸出到兩個地方(檔案及標準輸出)
  • 將man tee的結果導入到col -b的輸入,將col -b 的結果輸出tee程式,由tee分配到ls.txt檔案及STDOUT(螢幕)
[lcc09@localhost ~]$ man ls | col -b | tee ls.txt
Redirect mut pipes.png

tr 轉換字元

  • tr 轉換字元
[lcc09@localhost ~]$ tr 'a-z' 'A-Z' < .bash_history
# 將 .bash_history的英文字母a-z轉換成大寫的A-Z
[lcc09@localhost ~]$ cat .bash_history | tr 'a-z' 'A-Z'
# 這命令和上述的命令結果一樣

送出多行文字到STDIN

  • STDIN可以在命令列中使用 <
  • All text until WORD is sent to STDIN
  • Sometimes called a heretext
[lcc09@localhost ~]$ mail -s 'please call' root << END
> hello root
> good by
> END
[lcc09@localhost ~]$ 
  • ctrl+D 結束輸入

Scripting:for loop array

  • 迴圈的語法與應用

for 的語法文字串方式

  • for 的語法文字串方式
for 變數  in 數值1  數值2  數值3
do
    # 迴圈內容,$變數可以取用迴圈帶入的數值
    echo $變數
done

簡單的測試 for 迴圈示範

  • 程式碼,檔案 for.sh,請建立如下程式
[student@server1 ~]$ nano for.sh
#!/bin/bash
for NAME in root student mtchang
do 
    echo $NAME 
done
  • 賦予可執行權
[student@server1 ~]$ chmod +x for.sh 
  • 執行結果,變數把root student mtchang三個文字依序帶入迴圈並顯示
[student@server1 ~]$ ./for.sh 
root
student
mtchang

迴圈中使用Name的方式

  • 此範例為寄信給root、mtchang及student的使用者
[student@server1 ~]$ vi loop.sh 
#!/bin/bash
# for 迴圈開始,第1次載入 root ,第2次載入 mtchang,第3次載入 student
# 底下程式碼請注意空白、單引號及雙引號的不同
for NAME in root mtchang student
do
    # 將收件人$NAME存為變數ADDRESS
    ADDRESS="$NAME"
    # 將信件內容寫成變數MESSAGE
    MESSAGE='Projects are due today!'
    # mail 指令簡單用法
    # echo '內容' | mail -s '標題' 收件人
    echo $MESSAGE | mail -s Reminder $ADDRESS
done

for 的語法:數值方式

  • for 的語法:數值方式
for 變數  in {整數值..整數值}
do
    # 迴圈內容,$變數可以取用迴圈帶入的數值
    echo $變數
done

產生一各簡單的for迴圈

  • for 迴圈:產生 1 至 20的整數
[lcc09@localhost ~]$ vi num.sh
#!/bin/bash
for n in {1..20}; do
        echo $n
done

檢測目前網段的機器,簡單寫法

  • 「$?」變數代表前一各指令正確執行成功。
  1. $?=1 則前一各指令執行失敗
  2. $?=0 則前一各指令執行成功
for n in {1..5}; 
do
      h="192.168.3.$n"
      echo $h 
      # 讓 stdout 不輸出,畫面會比較乾淨
      # ping -c1 $h > /dev/null
      echo "return value = $? "
done

檢測目前網段的機器

  • 檢測目前網段的機器,存活的機器。
  • 「$?」變數代表前一各指令正確執行成功。
  1. $?=1 則前一各指令執行失敗
  2. $?=0 則前一各指令執行成功
#!/bin/bash
# alive2.sh
for n in {1..20}; do
host=192.168.1.$n
ping -c1 $host &> /dev/null
if [ $? = 0 ]; then
        echo "$host id UP"
else
        echo "$host id DOWN"
fi
done

變數值使用命令帶入

  • 在變數的部份,使用命令或是檔案帶入的情形
  • 先測是底下的指令,從 /etc/passwd 檔案找出帶有 bash 的行
[root@localhost ~]# grep bash /etc/passwd
root:x:0:0:root:/root:/bin/bash
lcc09:x:500:500:lcc09:/home/lcc09:/bin/bash
student:x:501:501::/home/student:/bin/bash
  • 寫個script將此指令結果帶入迴圈
[root@localhost ~]# vim foruser.sh
#!/bin/bash
for USER in $(grep bash /etc/passwd)
do
echo $USER
done
# 賦予可執行權限
[root@localhost ~]# chmod +x foruser.sh 
# 執行
[root@localhost ~]# ./foruser.sh 
root:x:0:0:root:/root:/bin/bash
lcc09:x:500:500:lcc09:/home/lcc09:/bin/bash
student:x:501:501::/home/student:/bin/bash

變數值使用文字檔案帶入

  • 使用指令 cat 觀看一個文字檔案
[root@localhost ~]# cat /etc/issue
Welcome !

Red Hat Enterprise Linux Server release 5.1 (Tikanga)
Kernel \r on an \m
  • 程式 forfile.sh
#!/bin/bash
for FILE in $(cat /etc/issue)
do
echo $FILE
done
  • 執行結果
[root@localhost ~]# ./forfile.sh 
Welcome
!
Red
Hat
Enterprise
Linux
Server
release
5.1
(Tikanga)
Kernel
\r
on
an
\m

Scripting:array

  • bash下array的幾種使用方法
#!/bin/bash
#一舉將變數設定到陣列中
array=(Redhat Novell MicroSoft Sun IBM HP Dell)

#利用for loop將陣列中的變數印出
for i in 0 1 2 3 4 5 6
do
echo "array[$i]=${array[$i]}"
done

#設定間隔符號為: 搭配$*將陣列的值一口氣輸出
IFS=:
echo "${array[*]}"

#設定間隔符號為換行,搭配$*將陣列的值一口氣輸出
IFS=$'\n'
echo "${array[*]}"

#將陣列中的值利用$@一口氣輸出與$*不同的是,不會將值合併成單一字串
echo "${array[@]}"

#印出陣列中有幾筆資料
echo "${#array[@]}"

執行結果:
array[0]=Redhat
array[1]=Novell
array[2]=MicroSoft
array[3]=Sun
array[4]=IBM
array[5]=HP
array[6]=Dell
Redhat:Novell:MicroSoft:Sun:IBM:HP:Dell
Redhat
Novell
MicroSoft
Sun
IBM
HP
Dell
Redhat Novell MicroSoft Sun IBM HP Dell
7 
張貼留言

like