只要是撰寫稍微大一點的程式,說真的很難避免掉一些檔案或目錄的操作,特別是檔案總管或FTP一類的程式更是會大量的使用。Tcl提供了數個非常容易使用而且功能強大的檔案系統操作命令,使用這些命令你可以很容易的維護檔案的屬性、目錄的列表或執行檔案複製、刪除或搬移...等等,甚至是用來寫個檔案總管也夠用了。這一篇文章將介紹cd、pwd、glob及file等4個Tcl裡很常用的檔案及目錄相關命令。
12.1 工作目錄操作
玩過Unix-like系統的人一定都會知道pwd及cd的功能,它們分別可以用來取得目前的工作目錄以及改變目前的工作目錄。在Tcl裡同樣也提供這兩個命令,而且功能也是相同的。pwd命令的語法:
pwd
pwd命令回傳目前的工作目錄,使用時不需要加任何參數。
cd命令的語法:
cd ?dirName?
cd命令用來切換目前的工作路徑到dirName指定的位置,dirName必需是一個存在的目錄,若dirName沒有指定則切換到家目錄。cd命令回傳空字串。
程式範例:
程式輸出:
/private/tmp /Users/dai
上面的程式輸出會根據正在使用的作業系統及狀況而有所差別。
12.2 目錄列表
Tcl使用glob命令來提供像dir或ls一樣的目錄列表命令,它的語法如下:glob ?switches? pattern ?pattern ...?
glob搜尋指定目錄下符合pattern的所有檔案及目錄,並以清單的方式回傳搜尋的結果。switches是選擇性的參數用來調整glob的功能,pattern是由關鍵字及特殊字元所組成的字串,它被用來比對搜尋的項目。以下是pattern可以使用的特殊字元:
? | 符合任意單一字元。 |
* | 符合任意長度的任意字元。 |
[chars] | 符合chars字元集合(字串)裡的任一字元。 |
\x | 符合字元x。 |
{a,b,...} | 符合任一大括號內的字串。 |
switches可以使用的參數如下:
-directory directory | 使用directory當成要搜尋的目標目錄,若沒有指定則使用目前的工作目錄。 |
-join | 以目錄分隔字元把所有的pattern組合為單一pattern,再執行搜尋。 |
-nocomplain | 如果glob命令執行時沒有找到任何檔案或目錄,它會抱怨找不到檔案。加上-nocomplain告訴glob不要產生抱怨訊息。 |
-path pathPrefix | 搜尋時把pathPrefix串接在pattern的開頭,且不解釋pathPrefix內的特殊字元。 |
-tails | 讓回傳的清單只包含檔名及目錄名稱而不包含路徑。 |
-types typeList | 限定搜尋的檔案類型,必需要為包含以下字元的清單:b(block special file)、c(character special file)、d(directory)、f(plain file)、l(symbolic link)、p(named pipe)、s(socket)、r(可讀)、w(可寫)、x(可執行)、readonly(唯讀)、hidden(隱藏檔)。 |
- - (短線間沒有空白) | switches的結束符號。 |
以下的範例在不同的電腦上可能會有不同的輸出,所以就不把輸出的結果打在文件上了,請大家自己試試看。
在Windows下列出c:/下所有的檔案及目錄:
在Linux下列出家目錄下所有的目錄:
在Linux下列出指定目錄下所有的MP3檔案:
在Windows下列出c:/mp3資料夾內的所有MP3檔案:
注意哦!! Tcl就算在Windows下目錄分隔的符號也要用「/」而不是「\」。
12.3 檔案及目錄基礎操作
file命令提供了一套檔案及目錄的處理功能,它讓你可以很輕鬆的執行檔案命名、刪除、搬移以及屬性設定...等操作。另外file也提供了數個檔案路徑相關的處理功能,例如:切割及組合檔案的路徑、讀取符號連結甚至是判別檔案所在的檔案系統...等等。在接下來的說明裡會常常出現「檔案路徑」這個名詞,如果沒有特別說明的話它是指檔案或目錄的路徑。□ 檔案及目錄複製
file copy提供了檔案及目錄複製的功能,它的語法如下:file copy ?-force? ?- -? source target
source是來源項目的檔案路徑,target是要複製的目的地。如果目的地已經有相同的項目,加上-force可以強迫覆蓋已經存在的項目,否則的話程式會產生錯誤。file copy有兩種運作模式:
- 如果target是一個已存在的目錄,那source會被複製到target目錄裡面。
- 如果target是一個不存在的檔案或目錄,那source被複製到target的父目錄下,並用target的檔名部份命名。
假設/tmp是一個已經存在的目錄,那下面的例子都會套用第一種運作模式:
假設b2.txt不存在。下面的例子都會套用第二種運作模式:
除了一次操作一個檔案外,file copy也允許你一次複製N個檔案。它的語法如下:
file copy ?-force? ?- -? source ?source ...? targetDir
source是要複製的來源,targetDir是要複製的目的地,所有的來源項目都會被複製到targetDir裡面。注意!! 在多個檔案的操作情況下,targetDir一定要是一個已經存在的目錄,否則程式會產生錯誤。-force同樣用來指定要複蓋已存在的項目。
程式範列:
□ 刪除檔案及目錄
file delete提供了檔案及目錄刪除的功能,它的語法如下:file delete ?-force? ?- -? pathname ?pathname ... ?
pathname是要刪除的項目,若要刪除的內容是目錄而且目錄裡還有其它的項目,指定-force表示要連同目錄裡的項目一併刪除。
程式範列:
□ 重新命名
file rename提供了檔案及目錄重新命名的功能,切確的說它的行為更像是檔案的搬移。事實上這個命令的用法和file copy是一樣的,只是file copy會保留來源項目,而file rename則會刪除來源的項目。它的語法也和file copy是一樣的:file rename ?-force? ?- -? source target file rename ?-force? ?- -? source ?source ...? targetDir
程式範列:
□ 建立檔案連結
file link提供了建立連結的功能,它的語法如下:file link ?-linktype? linkName ?target?
linkName是要建立的連結檔案,target是要連結的目標。如果沒有指定target的話,那file link會把linkName當成是一個已存在的連結檔案,然後回傳它的連結目標。linktype是要建立連結的種類, 它的值可以是「-symbolic」或「-hard」,預設是-symbolic。
Linux下的程式範列:
□ 讀取檔案連結
file readlink提供了讀取連結的功能,並回傳連結的目標路徑。它的語法如下:file readlink name
程式範例:
□ 建立目錄
file mkdir提供了建立目錄的功能。它的語法如下:file mkdir dir ?dir ...?
dir是要建立的目錄。如果要建立的目錄已經存在,file命令會產生錯誤。
程式範列:
□ 其它常用的操作
file exists可以用來判斷檔案或目錄是否存在,是的話回傳1,否則回傳0:file isdirectory 可以用來判斷某個檔案路徑是不是目錄,是的話回傳1,否則回傳0:
file isfile 可以用來判斷某個檔案路徑是不是檔案,是的話回傳1,否則回傳0:
file pathtype name :分析檔案路徑是absolute、relative還是volumerelative。
file size 回傳檔案或目錄所佔用的位元組數:
file type 可以取得檔案或目錄的類型,它的回傳值可能是:file、directory、characterSpecial、blockSpecial、fifo、link或是socket。
12.4 檔案名稱及路徑處理
file命令提供了數個與平台無關的檔案路徑操作功能,如果說你想要寫跨平台的程式,這些功能將會非常的管用,因為它們讓你不用在意路徑分隔是斜線、反斜線還是其它。注意!! 接下來說明的file命令都是對字串來操作,換句話說,就算檔案不是真的存在,這些命令同樣可以正常的運作。□ 取得檔案的副檔名
file extension分析檔案路徑,然後回傳副檔名的部份。程式的第2行路徑中並沒有包含副檔名,所以程式會輸出空字串。
程式輸出:
.txt
□ 取得檔案的父目錄
file dirname分析檔案路徑,然後回傳目錄的部份。程式的第一行因為沒有目錄的部份,所以會回傳「.」表示目前的目錄。
程式輸出:
. /Users
□ 取出檔案的檔名部份
file tail分析檔案路徑中的檔名部份,並回傳檔名部份。程式輸出:
a.txt
□ 移去檔案的副檔名
file rootname分析檔案路徑,然後回傳移去副檔名後的結果。程式輸出:
/Users/dai/a
□ 切割檔案路徑
file split切割檔案路徑並以清單的方式回傳切割後的結果。程式輸出:
/ Users dai a.txt
□ 組合檔案路徑
file join可以用來組合檔案路徑,並回傳組合後的結果。程式輸出:
/ Users/dai/a.txt
□ 正規化檔案路徑
file normalize會解析檔案路徑中的「..」及「.」符號,並回傳處理後的結果。程式輸出:
/a/b /a/b/c
□ 轉換路徑
file nativenam轉換檔案路徑為目前平台相依的格式。Linux及Mac下的程式輸出:
/Users/dai/a.txt
Windows下的程式輸出:
\Users\dai\a.txt
□ 取得路徑分隔符號
file separator傳回目前平台的路徑分隔符號。Linux及Mac下的程式輸出:
/
Windows下的程式輸出:
\
12.5 檔案及目錄的屬性處理
接下來要介紹檔案及目錄的屬性檔案。□ 存取時間屬性
file atime可以用來取得或是設定檔案或目錄的最後存取時間,回傳的時間是由1970/1/1開始算起的秒數。file mtime可以用來取得或是設定檔案或目錄的最後修改時間,回傳的時間是由1970/1/1開始算起的秒數。
程式第2行中的clock seconds會回傳自1970/1/1到目前的秒數。
□ 存取權限屬性
file attribute可以用來取得或是設定檔案的屬性。如果你要用attribute來取得所有可用的屬性可以這樣做:在我的Mac系統下它有如下的輸出:
-group staff -owner dai -permissions 00644 -readonly 0 -creator {} -type {} -hidden 0 -rsrclength 0
如果你要設定屬性可以這樣做:
file executable回傳檔案是否有可執行的權限。回傳1表示可執行,否則回傳0。
file readable回傳檔案或目錄是否有讀取的權限。回傳1表示可讀取,否則回傳0。
file writable回傳檔案或目錄是否有寫入的權限。回傳1表示可寫入,否則回傳0。
file owned name回傳檔案是否屬於目前使用者。回傳1表示是,否則回傳0。
□ 存取檔案的狀態
file lstat可以取得檔案的資訊,功能如同lstat系統乎叫。它的語法如下:file lstat name varName
file stat可以取得檔案的資訊,功能如同stat系統乎叫。它的語法如下:
file stat name varName
這兩個命令都會取得檔案或目錄name的狀態,並把取得的資訊以陣列的型式儲存在varName指定的變數裡。如下是取回的陣列元素:
atime | 檔案最後存取的時間。 |
ctime | 檔案狀態最後變更的時間。 |
dev | 檔案系統的裝置代號。 |
gid | 檔案所有群組的ID。 |
ino | i-node編號。 |
mode | 檔案屬性。 |
mtime | 檔案最後修改的時間。 |
nlink | 連結數量。 |
size | 檔案的大小。 |
type | 檔案的類型,和file type一樣。 |
uid | 檔案所有者的ID。 |
程式範例:
程式輸出:
data(ctime) = 1239428193 data(dev) = 234881026 data(gid) = 20 data(ino) = 192353 data(mode) = 16877 data(mtime) = 1239428193 data(nlink) = 42 data(size) = 1428 data(type) = directory data(uid) = 501
12.6 其它檔案處理功能
file channels可以列出已經開啟的檔案通道。它的語法如下:file channels ?pattern
file channels以清單的方式回傳符合pattern的通道代碼,如果不指定pattern的話會回傳所有的通道代碼。
file system用來分析檔案儲存在哪一種檔案系統下。
在Windows XP下,這個程式應該會輸出:
native NTFS
file volumes以清單的方式列出目前已掛載的檔案系統目錄
在我的Windows下它輸出了:
A:/ C:/ D:/ X:/
按右上方的「#」號切換側邊欄
32 個意見
餅乾 | 2009年11月4日 晚上7:23
請問大大:
假若我要在我的tcl程式當中,
呼叫我的.c的檔案,
需使用什麼指令呢?
謝謝大大的回答。
Dai | 2009年11月5日 晚上7:19
你需要先把.c檔編譯成tcl可以載入的dll或是so檔案,然後再載入tcl使用,你可以參考swig。
sam | 2010年1月20日 下午1:57
Dai大大,請問一下,SWIG是不是一定要在unix like下的環境下才能執行丫,若我是用Windows的OS,那我是不是要灌Cygwin呢,因為我有用MinGW灌gcc/g++在電腦裡,照著Dai大大學弟(K.Y)的那一篇教學,走到下面這一步發生錯誤:
$gcc -fpic -shared -static hello.c hello_wrap.c -I/opt/ActiveTcl-8.6/include -o hello.so
用MinGw灌的gcc沒有問題可以編一般的.c檔案,
錯誤訊息應該是標頭檔或LIB沒讀到,我想我的路徑設定有問題:
是改成-I/C:\Tcl\include這樣子嗎?
謝謝Dai大大...
dai | 2010年1月20日 下午2:47
hi sam .
如果是在windows下用gcc的話,我比較愛用的是msys_mingw它和cygwin不一樣的是它可以編出原生的程式,在sourceforge的tcl主頁可以下載
http://sourceforge.net/projects/tcl/files/
我是用0.8版本的msys
我編譯的命令是:
D:>gcc -shared hello.c hello_wrap.c -o hello.so -IC:\Tcl\include -LC:\Tcl\lib -ltcl86
因為我裝的是8.6版的Tcl所以我用 -ltcl86,這樣的話可以正確的編出hello.so
sam | 2010年1月20日 下午3:22
Dai大大,
成功了ㄟ,用大大的編譯指令就可以了...
不知道是不是靜態連結的關係,到時再試試...
謝謝Dai...
dai | 2010年1月20日 下午5:35
哈 ~ 恭喜你成功了!!
我猜測可能是 -LC:\Tcl\lib -ltcl86 這兩個參數的問題。
Unknown | 2010年3月24日 下午3:36
我的code:
cd phy
exec run.exe
run.exe 是在./phy下
可是run 這個tcl 程式卻沒執行
請問各位有遇過這個問題嗎
dai | 2010年3月24日 下午4:58
你是在windows下吧!! 那應該是run.exe有問題!! 也許是run.exe本身就跑不起來。例如它要相依的dll找不到吧!!
豬事大吉 | 2010年9月10日 上午11:24
我是在linux環境設計TCL/TK
使用tk_getOpenFile可以出現視窗選擇檔案
請問我若想選擇目錄,是否有解法?
sam | 2010年9月10日 中午12:36
Hi 大吉:
可以使用tk_chooseDirectory來達到這個效果,範例如下:
set dir [tk_chooseDirectory -initialdir ~ -title "Choose a directory"]
if {$dir eq ""} {
label .lb -text "No directory selected"
} else {
label .lb -text "Selected $dir"
}
grid .lb
dai | 2010年9月12日 上午10:51
to sam:
這邊就靠你幫忙了....我現在人在馬祖
要3個月才能回台灣一次
麻煩了 感謝
這邊的網咖 好貴阿 當兵會當到 虧本
dai
豬事大吉 | 2010年9月17日 下午1:40
TO: sam
謝謝你的解答~~
我會用"tk_chooseDirectory"~~
匿名 | 2011年6月15日 晚上7:51
你好
想請教一下
在tclsh打pwd之後,它顯示 "C:/Documents and Settings/user"
就是說如果做檔案I/O操作的話,tcl會以該目錄當作基準點是嗎?
那假如要切換工作目錄的話,該怎麼切換才好?
dai | 2011年6月16日 下午1:38
hi,
你可以使用
cd "c:/xxx/xx"
切到其它的目錄
匿名 | 2011年7月21日 晚上9:03
12.2目錄列表的引數
- - (短線間沒有空白) switches的"束結"符號。
匿名 | 2011年7月22日 下午3:20
小妹我是位TCL新手,因為工作需求最近需要撰寫TCL script透過com port對DUT下指令,期間遇到個棘手的問題。
使用read將command讀出來後再餵給DUT是ok的,但有時速度太快,DUT會來不及吃到指令。如果用get讀command則會因為缺少換行指令,一直只讀到第一行,即使後來加了 \n 也沒有用,曾試著要用ascii 下enter的指令,但不知道要怎麼用,還是可以puts 指令時一次下幾個字元,或加after??? 請dai大幫忙
dai | 2011年7月22日 晚上9:36
hi,
使用read將command讀出來後再餵給DUT是ok的,但有時速度太快,DUT會來不及吃到指令。
妳是指餵給DUT的速度太快,以致於DUT來不及處理嗎? 如果是這樣的話~妳把要送給DUT的命令都先儲存在Tcl裡頭,然後再每次送資料給DUT之後,都用after xxxx 先delay一段時間,再送下一個指令,試試看。
關於妳說的: 如果用get讀command則會因為缺少換行指令,一直只讀到第一行,即使後來加了 \n 也沒有用。
正常用情況不管是read或是get讀取檔案或是comm port,應該都不會重複讀到第一行才對,除非妳用seek命令把檔案讀取指標移到檔案的開頭。 妳有這麼做嗎?
另外!!puts命令會根據妳正在執行的作業系統自動加上換行,lf、cr或crlf,如果妳不希望puts命令自動幫妳加上這些斷行字元的話,可以把要輸出的channel用fconfigure命令的-translation 選項,把讓puts命令操作在binary模式。這樣你puts就會原封不動輸出妳給的資料。
ps. 也許妳在送出每個指令時需要使用flush命令。
匿名 | 2011年9月26日 晚上7:30
您好~~想請問一下Dai大,可透過 tk 執行tcl?
為了測試公司的產品寫了很多隻TCL FILE,想再利用TK做個管理小工具,但不知要怎麼 執行TCL FILE..
dai | 2011年9月26日 晚上10:00
你可以用包含tk的直譯器wish來啟動tcl的檔案,例如在console執行下方的命令:
wish xxx.tcl
匿名 | 2011年12月4日 上午11:18
感謝戴兄幫助,教我銜接C++,讓我繼續堅持TCL到12章了。上MSN時記得給我留言。
想問兩個問題。
1.目前需要幫客戶做,記錄工廠生產流程表單的軟體。需要資料庫,TCL有沒有提供可以儲存表單的資料庫教學?
2.TCL有沒有不要資料庫,也可以紀錄表單文件的方法?
crazystudent.tw@yahoo.com.tw 留
dai | 2011年12月4日 下午2:08
哇 ~ 你用Tcl來接案寫程式嗎? 真開心遇到同行了 哈 ~
嗯 ~ 你可以用檔案儲存的方式 來記錄表單的資料,這是最簡單的方法。
或是說 你也可以用sqlite或是MySQL來儲存 ,請參考tdbc : http://wiki.tcl.tk/20343
我有上msn但都沒有看到你上線...會不會是加失敗了??
匿名 | 2011年12月4日 下午3:45
你的帳號不是 adai1115@hotmail.com?
匿名 | 2011年12月4日 下午3:54
我兩個msn: crazystudent.tw@yahoo.com.tw and canfree987@hotmail.com.tw
jky777 | 2012年4月17日 上午9:23
file pathtype name :分"枌"檔案路徑是absolute、relative還是volumerelative。
這個字 "枌" ?
豬事大吉 | 2013年4月25日 下午1:15
請問
因為glob似乎只能搜尋該層目錄下符合的檔案或目錄
如果要找該層目錄下所有目錄下均符合的檔案或目錄
要怎麼做?
dai | 2013年4月25日 下午2:01
這需要使用程式語言中「遞迴」的技巧~才有辦法!!
匿名 | 2013年7月3日 下午1:54
請問DAI大大
我原程式碼是set filename [tk_getOpnfile -filetype $::filetype]
我要如何直接去指定某個黨名
dai | 2013年7月3日 下午3:28
官方手冊中的頁面尾巴有-filetypes的範例 :
http://www.tcl.tk/man/tcl8.6/TkCmd/getOpenFile.htm
預設的目錄位置及檔名可以用下列的兩個選項 :
-initialdir directory
-initialfile filename
匿名 | 2013年11月22日 上午9:45
飲用本文"如果target是一個不存在的檔案或目錄,那source被複製到taget的父目錄下,並用taget的檔名部份命名。"
後面兩個應該是"target"
dai | 2013年11月22日 下午3:00
好的謝謝~
匿名 | 2016年9月8日 上午10:38
Hi
請問一下,在windows系統下,如果要知道一個資料夾(例如: C:)和其中包含的所有檔案總共占的空間是多少,指令怎麼下呀?
感謝大大們賜教,謝謝。
匿名 | 2018年3月30日 晚上11:16
版大你好
我有個文件如下:
=========
text1
=========
aa bb cc
=========
text2
=========
dd ee ff
我想輸出成2個文件
文件一的名稱叫:text1 內容為:aa bb cc
文件二的名稱叫:text2 內容為:dd ee ff
該如何寫呢??謝謝
留下您的意見