免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: 網中人

[学习共享] shell 十三問? [复制链接]

论坛徽章:
0
发表于 2004-10-30 11:23 |显示全部楼层

shell 十三問?

能在论坛上跟大家学习学习是我的荣幸,请多指教!!!

论坛徽章:
0
发表于 2004-10-31 01:06 |显示全部楼层

shell 十三問?

看完了,好舒服:)
等待网中人再来指点我们

论坛徽章:
0
发表于 2004-11-05 16:52 |显示全部楼层

shell 十三問?

请问哪里还可以下到13问的简体版,谢谢

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
发表于 2004-11-05 21:40 |显示全部楼层

shell 十三問?

b1) [^ ] 跟 [! ] 差在哪?

這個問題等了好久都沒人出來補充, 而我呢, 也被追殺了好幾回...  ^_^
趁著今晚有一點空閒, 趕快將此樁心事做一了結吧...

這道題目說穿了, 就是要探討 Wildcard 與 Regular Expression 的差別的.
這也是許多初學 shell 的朋友很容易混亂的地方.
首先, 讓我們回到十三問之第 2 問, 再一次將我們提到的 command line format 溫習一次:
  1. command_name options arguments
复制代码

同時, 也再來理解一下我在第 5 問所提到的變量替換的特性:
  1. 先替換, 再重組 command lline!
复制代码

有了這兩道基礎後, 才讓我們來看看 wildcard 是甚麼回事吧.

Part-I: Wildcard

首先, wildcard 也是屬於 command line 的處理工序, 作用於 argument 裡的 path 之上.
沒錯, 它不用在 command_name 也不用在 options 上.
而且, 若 argument 不是 path 的話, 那也與 wildcard 無關.
換句更為精確的定義來講, wildcard 是一種命令行的路逕擴展(path expansion)功能.
提到這個擴展, 那就不要忘記了 command line 的"重組"特性了!
是的, 這與變量替換(variable substitution)及命令替換(command substitution)的重組特性是一樣的!
也就是在 wildcard 進行擴展後, 命令行會先完成重組才會交給 shell 來處理.

了解了 wildcard 的擴展與重組特性後, 接下來, 讓我們了解一些常見的 wildcard 吧:
*: 匹配 0 或多個字元
?: 匹配任意單一字元
    : 匹配 list 中的任意單一字元(註一)
    [!list]: 匹配不在 list 中的任意單一字元
    {string1,string2,...}: 匹配 sring1 或 string2 (或更多)其一字串
    (註一: list 可以為指定的個別字元, 如 abcd; 也可以為一段 ASCII 字元的起止範圍, 如: a-d .)

    例:
    a*b: a 與 b 之間可以有任意長度的任意字元, 也可以一個也沒有, 如: aabcb, axyzb, a012b, ab 等.
    a?b: a 與 b 之間必須也只能有一個字元, 可以是任意字元, 如: aab, abb, acb, a0b 等.
    a[xyz]b: a 與 b 之間必須也只能有一個字元, 但只能是 x 或 y 或 z, 如: axb, ayb, azb 這三個.
    a[!0-9]b: a 與 b 之間必須也只能有一個字元, 但不能是阿拉伯數字, 如: axb, aab, a-b 等.
    a{abc,xyz,123}b: a 與 b 之間只能是 abc 或 xyz 或 123 這三個字串之一, 如 aabcb, axyzb, a123b 這三個.

    注意:
    1) [! ] 中的 ! 只有放在第一順位時, 才有排除之功. 舉例說:
    [!a]* 表示當前目錄下所有不以 a 開首的路逕名稱.
    /tmp/[a\!]* 表示 /tmp 目錄下以 a 或 ! 開首的路逕名稱. (思考: 為何 ! 前面要加 \ 呢? 提示: 十三問之 4 )

    2) [ -] 中的 - 左右兩邊均有字元時, 才表示一段範圍, 否則僅作 "-"(減號) 字元來處理. 舉例說:
    /tmp/*[-z]/[a-zA-Z]* 表示 /tmp 目錄下所有以 z 或 - 結尾的子目錄下以英文字母(不分大小寫)開首的路逕名稱.

    3) 以 * 或 ? 開首的 wildcard 不能匹配隱藏文件(即以 . 開首的文件). 舉例說:
    *.txt 並不能匹配 .txt 但可匹配 1.txt 這樣的路逕名稱.
    但 1*txt 及 1?txt 均可匹配 1.txt 這樣的路逕名稱.

    基本上, 要掌握 wildcard 並不難, 只要多加練習, 再勤於思考, 就能熟加運用了.
    再次提醒: 別忘了"擴充+重組"這個重要特性, 而且只作用在 argument 的 path 上.
    比方說, 假設當前目錄下有 a.txt b.txt c.txt 1.txt 2.txt 3.txt 這幾份文件.
    當我們在命令行中下達 ls -l [0-9].txt 的命令行時,
    因為 wildcard 處於 argument 的位置上, 於是根據其匹配的路逕, 擴展為 1.txt 2.txt 3.txt ,
    再重組出 ls -l 1.txt 2.txt 3.txt 這樣的命令行.
    因此, 你在命令行上敲 ls -l [0-9].txt 與 ls -l 1.txt 2.txt 3.txt 都是同樣的結果, 其原因正是於此了... :)


    Part-II: Regular Expression
    http://bbs.chinaunix.net/viewthr ... ;page=16#pid2934852


    ----------------
    (順道一提: eval )

    講到 command line 的重組特性, 真的需要我們好好的加以理解的.
    如此便能抽絲剝襺的一層層的將整個 command line 分析得一清二楚, 而不至於含糊.
    假如這個重組特性理解下來, 那麼, 接下來我們介紹一個好玩的命令 --- eval .

    我們在不少變量替換的過程中, 常碰到所謂的複式變量的問題, 如:
    1. a=1
    2. A1=abc
    复制代码

    我們都知道 echo $A1 就可得到 abc 這個結果.
    然而, 我們能否用 $A$a 來取代 $A1 而同樣替換出 abc 呢?

    這個問題我們可用很輕鬆的用 eval  來解決:
    1. eval echo \$A$a
    复制代码

    說穿了, eval 只不過是在命令行完成替換重組後, 再來一次替換重組罷了...
    就是這麼簡單啦~~~   ^_^

    [ 本帖最后由 網中人 于 2008-11-4 02:02 编辑 ]

论坛徽章:
0
发表于 2004-11-05 22:48 |显示全部楼层

shell 十三問?

谢谢netman,不过有点看不懂,再看几遍13问,

期待精彩~

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
发表于 2004-11-07 20:56 |显示全部楼层

shell 十三問?

Part-II: Regular Expression

接下來的 Regular Expression(RE) 可是個大題目, 要講的很多, 我這裡當然不可能講得很完全.
只希望帶給大家一個基本的入門概念, 就很是足夠了...

先來考一下英文好了: What is expression?
簡單來說, 就是"表達", 也就是人們在溝通時所要陳述的內容.
然而, 生活中, 表達方要清楚的將意思描述清楚而讓接收方完整且無誤的領會, 可不是件容易的事情.
因而才會出現那麼多的"誤會", 真可嘆句"表達不易"啊....
同樣的情形也發生在電腦的資料處理過程中, 尤其是當我們在描術一段"文字內容"的時候...
那麼, 我們不禁要問: 有何方法可以讓大家的誤會降至最低程度而讓表達的精確度達到最高程度呢?
答案就是"標準化"了, 亦就是我們這裡要談的 Regular Expression 啦....  ^_^

然而, 在進入 RE 介紹之前, 不防先讓我們溫習一下 shell 十三問第 4 問, 也就是關於 quoting 的部份.
關鍵是要能夠區分 shell command line 上的 meta 與 literal 這兩種不同的字符類別.
然後, 我這裡才跟你講:
--- RE 表達式裡的字符也是分為 meta 與 literal 這兩種!
呵, 不知親愛的讀者是否被我搞混亂了呢? ...  ^_^
這也難怪啦, 因為這的確是最容易混亂的地方, 剛學 RE 的朋友很多時候都死在這裡!
因此請特別小心理解哦...
簡單而言, 除非你將 RE 寫在特定程式使用的腳本裡,
否則, 我們的 RE 也是透過 command line 輸入的.
然而, 不少 RE 所始用的 meta 字符, 跟 shell meta 字符是衝突的.
比方說, * 這個字符, 在 RE 裡是一個 modifier(後述), 在 command line 上, 卻是個 wildcard !

那麼, 我們該如何解決這樣的衝突呢? 關鍵就是看你對十三問第 4 問所提的 quoting 是否夠理解了!
若你明白到 shell quoting 就是在 command line 上關閉 shell meta 這一基本原理,
那你就能很輕鬆的解決 RE meta 與 shell meta 的衝突問題了:
--- 用 shell quoting 關掉 shell meta 就是了!
就這麼簡單...  ^_^
再以剛提到的 * 字符為例, 若在 command line 中沒有 quoting 處理的話, 如 abc* ,
那就會被作為 wildcard expansion 來擴充及重組了.
若將之置於 quoting 中, 如 "abc*", 則可避免 wildcard expansion 的處理.

好了, 說了大半天, 還沒進入正式的 RE 介紹呢...
大家別急, 因為我的教學風格就是要先建立基礎, 循序漸進的...  ^_^
因此, 我這裡還要在囉唆一個觀念, 才會到 RE 的說明啦... (哈... 別打我....)
當我們在談到 RE 時, 千萬別跟 wildcard 搞混在一起!
尤其在 command line 的位置裡, wildcard 只作用於 argument 的 path 上.
但是 RE 卻只用於"字串處理"的程式之中, 這與路逕名稱一點關系也沒有!
RE 所處理的字串通常是指純文檔或透過 stdin 讀進的內容...

okay, 夠了夠了, 我已看到一堆人開始出現不大耐煩的樣子了....  ^_^
現在, 就讓我門登堂入室, 撩開 RE 的神秘面紗吧, 這樣可以放過我了吧? 哈哈...

在 RE 的表達式裡, 主要分兩種字符(character): literal 與 meta.
所謂 literal 就是在 RE 裡不具特殊功能的字符, 如 abc, 123 這些;
而 meta 在 RE 裡具有特殊的功能, 要關閉之, 需在 meta 前面使用 escape( \ )字符.

然而, 在介紹 meta 之前, 先讓我們來認識一下字符組合(character set)會更好些.
所謂的 char. set 就是將多個連續的字符作一個集合, 比方說:
abc: 表示 abc 三個連續的字符, 但彼此獨立而非集合. (可簡單視為三個 char. set)
(abc): 表示 abc 這三個連續字符的集合. (可簡單視為一個 char. set)
abc|xyz: 表示或 abc 或 xyz 這兩個 char. set 之一.
[abc]: 表示單一字符, 可為 a 或 b 或 c . (與 wildcard 之 [abc] 原理相同)
[^abc]: 表示單一字符, 不為 a 或 b 或 c 即可. (與 wildcard 之 [!abc] 原理相同)
. : 表示任意單一字符. (與 wildcard 之 ? 原理相同)

在認識了 char. set 這個概念後, 然後再讓我們多認識幾個 RE 中常見的 meta 字符:

- 錨點(anchor)
用以標識 RE 於句子中的位置所在. 常見有:
^: 表示句首. 如 ^abc 表示以 abc 開首的句子.
$: 表示句尾. 如 abc$ 表示以 abc 結尾的句子.
\<: 表示詞首. 如 \<abc 表示以 abc 開首的詞.
\>: 表示詞尾. 如 abc\> 表示以 abc 結尾的詞.

- 修飾字符(modifier)
獨立表示時本身不具意義, 專門用以修改前一個 char. set 的出現次數. 常見有:
*: 表示前一個 char. set 的出現次數為 0 或多次. 如 ab*c 表示 a 與 c 之間可有 0 或多個 b 存在.
?: 表示前一個 char. set 的出現次數為 0 或 1 次. 如 ab?c 表示 a 與 c 之間可有 0 或 1 個 b 存在.
+: 表示前一個 char. set 的出現次數為 1 或多次. 如 ab+c 表示 a 與 c 之間可有 1 或多個 b 存在.
{n}: 表示前一個 char. set 的出現次數必須為 n 次. 如 ab{3}c 表示 a 與 c 之間必須有 3 個 b 存在.
{n,}: 表示前一個 char. set 的出現次數至少為 n 次. 如 ab{3,}c 表示 a 與 c 之間至少有 3 個 b 存在.
{n,m}: 表示前一個 char. set 的出現次數為 n 到 m 次. 如 ab{3,5}c 表示 a 與 c 之間有 3 到 5 個 b 存在.

然而, 當我們在識別 modifier 時, 卻很容易忽略"邊界(boundary)"字符的重要性.
以剛提到的 ab{3,5}c 為例, 這裡的 a 與 c 就是邊界字符了.
若沒有邊界字符的幫忙, 我們很容以作出錯誤的解讀.
比方說: 我們用 ab{3,5} 這個 RE (少了 c 這個邊界字符)可以抓到 abbbbbbbbbbc (a 後有 10 個 b )這串字嗎?
從剛才的 modifier 我們一般會認為我們要的 b 是 3 到 5 個, 若超出了此範圍, 就不是我們要表達的.
因此, 我們或會很輕率的認為這個 RE 抓不到結果(上述“abbbbbbbbbbc”字串)...
然而答案卻是可以的! 為甚麼呢?
讓我們重新解讀 ab{3,5} 這個 RE 看看:
我們要表達的是 a 後接 3 到 5 個 b 即可, 但 3 到 5 個 b 後面我們卻沒規定是甚麼,
因此在 RE 後面可以是任意的文字, 當然包括 b 也可以啦! (明白了嗎?)
同樣的, 我們用 b{3,5}c 也同樣可以抓到 abbbbbbbbbbc 這串字的.
但我們若使用 ab{3,5}c 這樣的 RE 時, 由於同時有 a 與 c 這兩個邊界字符, 那就截然不同了!

有空再思考一下, 為何我們用下面這些 RE 都可抓到 abc 這串字呢?
x*
ax*, abx*, ax*b
abcx*, abx*c, ax*bc
bx*c, bcx*, x*bc
...(還有更多...)
但, 若我們在這些 RE 前後分別加一個 ^ 與 $ 這樣的 anchor, 那又如何呢?

剛學 RE 時, 只要能掌握上面這些基本的 meta 大蓋就可以入門了.
一如前述, RE 是一種規範化的文字表達方式, 主要用於某些文字處理工具之間,
如 grep, perl, vi, awk, sed, 等等. 常用以表示一段連續的字串, 捕獲之或替換之.
然而, 每種工具對 RE 表達式的具體解讀或有一些細微差異, 不過, 基本原則還是一致的.
只要能掌握 RE 的基本原理, 那就一理通百理明了, 只是在實作時稍加變通即可.

比方以 grep 來說, 在 Linux 上你可找到 grep, egrep, fgrep 這幾個程式, 其差異大致如下:

* grep:
傳統的 grep 程式, 在沒有參數的情況下, 只輸出符合 RE 字串之句子. 常見參數如下:
-v: 逆反模示, 只輸出"不含" RE 字串之句子.
-r: 遞迴模式, 可同時處理所有層級子目錄裡的文件.
-q: 靜默模式, 不輸出任何結果(stderr 除外. 常用以獲取 return value, 符合為 true, 否則為 false .)
-i: 忽略大小寫.
-w: 整詞比對, 類似 \<word\> .
-n: 同時輸出行號.
-c: 只輸出符合比對的行數.
-l: 只輸出符合比對的文件名稱.
-o: 只輸出符合 RE 的字串. (gnu 新版獨有, 不見得所有版本都支持.)
-E: 切換為 egrep .

* egrep:
為 grep 的擴充版本, 改良了許多傳統 grep 不能或不便的操作. 比方說:
- grep 之下不支持 ? 與 + 這兩種 modifier, 但 egrep 則可.
- grep 不支持 a|b 或 (abc|xyz) 這類"或一"比對, 但 egrep 則可.
- grep 在處理 {n,m} 時, 需用 \{ 與 \} 處理, 但 egrep 則不需.
諸如此類的... 我個人會建議能用 egrep 就不用 grep 啦...  ^_^

* fgrep:
不作 RE 處理, 表達式僅作一般字串處理, 所有 meta 均失去功能.

好了...
關於 RE 的入門, 我暫時就介紹到這裡.
雖然寫得有點亂, 且有些觀念也不很精確, 不過, 姑且算是對大家有一個交差吧.... ^_^
若這兩天還有時間的話, 我再舉些範例來分析一下, 以助大家更好的理解.
假如更有可能的話, 也順道為大家介紹一下 sed 這個工具.
(啊, 這次我不敢作保證了哦... ^_^ )

[ 本帖最后由 網中人 于 2009-3-24 10:25 编辑 ]

论坛徽章:
0
发表于 2004-11-12 10:26 |显示全部楼层

shell 十三問?

太精彩了,期待有下回关于sed的讲解。自己看文档,看了就忘了,显然是不得要领。有了精彩的解说,真是事半功倍啊。

论坛徽章:
0
发表于 2004-11-12 11:35 |显示全部楼层

shell 十三問?

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
发表于 2004-11-14 20:49 |显示全部楼层

shell 十三問?

想要简体版的:把文本考下来,到word里转换一下就成简体了

读网中人十三问的第四问的理解:
1、变量一定得用""
2、处理顺序要搞清楚:这两行一定要牢牢记在脑中
命令格式
command-name options argument
处理过程:
shell 会依据 IFS(Internal Field Seperator) 将 command line 所输入的文字给拆解为"字段"(word)。
然后再针对特殊字符(meta)先作处理,
最后再重组整行 command line 。


3、例子:
空格的好理解,但CR字符不好理解,如'',""
  1.    $ A='B
  2.    > C
  3.    > '
  4.    $ echo "$A"
  5.    B
  6.    C
  7.    $ echo $A
  8.    B C
复制代码


echo 的$A加上soft quote后,得出的结果不同了,
第一个是断行字符(new line),取消了CR和IFS的功能
第二个应该是一个空格了,仅取消CR功能,而保留IFS功能
第三个是CR

原因如下:
然而,由于 echo $A 时的变量没至于 soft quote 中,因此当变量替换完成后并作命令行重组时,<enter> 会被解释为 IFS (空格键),而不是解释为 New Line (换行符)字符。

而在escape中
  1.    $ A=B\
  2.    > C\
  3.    >
  4.    $ echo $A
  5.    BC
  6.    $ echo "$A "
  7.    BC
复制代码


得出的结果是BC,原因:
<enter> 键本身在 shell meta 中的特殊性,在 \ 跳脱后面,仅仅取消其 CR 功能,而不会保留其 IFS 功能(空格)。 因此就是(NULL)

因此在上面两个例子中 <enter> 键所产生的字符有四种:
CR (结束命令)
IFS (空格)
NL(New Line) (断行)
NULL (空)



不知我的理解是否正确,还望各位指点,呵呵,我感觉这样说好像更容易理解
对了,想要简体版的,只需将所以拷下来到word转换一下就行 了

论坛徽章:
0
发表于 2004-11-16 16:37 |显示全部楼层

shell 十三問?

首页上面的简体版好像不能down哦
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP