為什麼 "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:有些 shell 會提供就自己的變數替代功能,那就可以不用 "basename", 而用更簡單的迴圈了:foreach f ( *.foo ) set base=`basename $f .foo` mv $f $base.bar endBourne Shell:
for f in *.foo; do base=`basename $f .foo` mv $f $base.bar done
C Shell:如果沒有 "basename" 可用或是想要做像把 foo.* 改名為 bar.* 之類的 事,那麼可以用其他的方法如 "sed" 把原來的檔案名稱做分隔的動作,但 是迴圈的想法是一樣的。你也可以利用 "sed" 把檔名轉換成 "mv" 的命令 ,然後再把這些命令轉給 "sh" 執行。如下:foreach f ( *.foo ) mv $f $f:r.bar endKorn Shell:
for f in *.foo; do mv $f ${f%foo}bar done
ls -d *.foo | sed -e 's/.*/mv & &/' -e 's/foo$/bar/' | sh1990 年 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 $_; }