next up previous
Next: 為什麼我用 "rsh host command" 會有一些奇怪的訊息出現? Up: 初學者可能會問的基本問題 Previous: 當我在寫 shell script 時,要如何從 terminal 讀入字元?

怎麼樣把 "*.foo" 改名為 "*.bar" 呢?怎樣把檔案名稱改成小寫呢?

為什麼 "mv *.foo *.bar" 不對呢? 想想 shell 是怎樣把萬用字元展開的 吧。 在 mv 讀取參數前 "*.foo" 與 "*.bar" 就已經展開了。 "mv *.foo *.bar" 在各種不同的 shell 會有不同的結果。 Csh 的話會印出 "No match",因為找不到 "*.bar"。 Sh 則是會執行 "mv a.foo b.foo c.foo *.bar",也就是說如果只有在有一名為 "*.bar" 的子目錄存在時 mv 才會 認為執行成功,不過就算成功也不是你所預期的結果。

正確的做法是用你所用的 shell 提供的迴圈來做。若你的系統中有  "basename" 這個指令:

C Shell:
		foreach f ( *.foo )
		    set base=`basename $f .foo`
		    mv $f $base.bar
		end

Bourne Shell:

		for f in *.foo; do
		    base=`basename $f .foo`
		    mv $f $base.bar
		done
有些 shell 會提供就自己的變數替代功能,那就可以不用 "basename", 而用更簡單的迴圈了:
C Shell:
		foreach f ( *.foo )
		    mv $f $f:r.bar
		end

Korn Shell:

		for f in *.foo; do
		    mv $f ${f%foo}bar
		done
如果沒有 "basename" 可用或是想要做像把 foo.* 改名為 bar.* 之類的 事,那麼可以用其他的方法如 "sed" 把原來的檔案名稱做分隔的動作,但 是迴圈的想法是一樣的。你也可以利用 "sed" 把檔名轉換成 "mv" 的命令 ,然後再把這些命令轉給 "sh" 執行。如下:
	        ls -d *.foo | sed -e 's/.*/mv & &/' -e 's/foo$/bar/' | sh
1990 年 4 月, Vladimir Lanin 把他自己寫的一隻叫 "mmv" 的程式  post 到 comp.sources.unix (Volumn 21, issues 87 and 88),這隻程式 就能夠把這件事處理得很好。 你可以這樣使用:
        	mmv '*.foo' '=1.bar'
以上所提的 shell 中的迴圈也可以用來做檔案名稱的大、小寫轉換。你可 以用改檔名的方式把大寫檔名改為小寫:

C Shell:

            foreach f ( * )
                mv $f `echo $f | tr '[A-Z]' '[a-z]'`
            end

Bourne Shell:

            for f in *; do
                mv $f `echo $f | tr '[A-Z]' '[a-z]'`
            done

Korn Shell:

            typeset -l l
            for f in *; do
                l="$f"
                mv $f $l
            done
如果你還希望能處理含有特殊字元(空白或其他的奇怪字元)的檔名,那 麼你最好用:

Bourne Shell:

            for f in *; do
              g=`expr "xxx$f" : 'xxx\(.*\)' | tr '[A-Z]' '[a-z]'`
              mv "$f" "$g"
            done
'expr' 不管檔名裡有沒有特殊字元都會印出檔名。

有些版本的 "tr" 需要用 '[' 和 ']',有些則不必。不過,不管是不是一 定要用 '[' 與 ']' 的 "tr",加了總是沒有害處。

若你的系統裡有裝 "perl",那你可以用 Larry Wall 寫的這個多用途改檔 名的程式。

	#!/usr/bin/perl
	#
	# rename script examples from lwall:
	#       rename 's/\.orig$//' *.orig
	#       rename 'y/A-Z/a-z/ unless /^Make/' *
	#       rename '$_ .= ".bad"' *.f
	#       rename 'print "$_: "; s/foo/bar/ if <stdin> =~ /^y/I' *

	$op = shift;
	for (@ARGV) {
	     $was = $_;
	     eval $op;
	     die $@ if $@;
	     rename($was,$_) unless $was eq $_;
	}



Tan Koan-Sin
1999-03-02