#

只要是撰寫稍微大一點的程式,說真的很難避免掉一些檔案或目錄的操作,特別是檔案總管或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有兩種運作模式:

  1. 如果target是一個已存在的目錄,那source會被複製到target目錄裡面。
  2. 如果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

該如何寫呢??謝謝

留下您的意見

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates