# 输入&输出

> 原文： [https://zetcode.com/lang/tcl/io/](https://zetcode.com/lang/tcl/io/)

在本章中，我们将使用 Tcl 中的输入和输出操作。 Tcl 有几个执行 io 的命令。 我们将介绍其中的一些。

Tcl 使用称为通道的对象读取和写入数据。 可以使用`open`或`socket`命令创建通道。 Tcl 脚本可使用三个标准通道，而无需显式创建它们。 操作系统会为每个新应用自动打开它们。 它们是`stdin`，`stdout`和`stderr`。 脚本使用标准输入`stdin`来读取数据。 脚本使用标准输出`stdout`写入数据。 脚本使用标准错误`stderr`来编写错误消息。

在第一个示例中，我们将使用`puts`命令。 它具有以下概要：

```tcl
puts ?-nonewline? ?channelId? string

```

`channelId`是我们要在其中写入文本的通道。 `channelId`是可选的。 如果未指定，则采用默认值`stdout`。

```tcl
#!/usr/bin/tclsh

puts "Message 1"
puts stdout "Message 2"
puts stderr "Message 3"

```

`puts`命令将文本写入通道。

```tcl
puts "Message 1"

```

如果未指定`channelId`，则默认情况下写入`stdout`。

```tcl
puts stdout "Message 2"

```

此行与上一行相同。 我们仅明确指定了`channelId`。

```tcl
puts stderr "Message 3"

```

我们写入标准错误通道。 错误消息默认情况下转到终端。

```tcl
$ ./printing.tcl 
Message 1
Message 2
Message 3

```

示例输出。

## `read`命令

`read`命令用于从通道读取数据。 可选参数指定要读取的字符数。 如果省略，该命令将从通道读取所有数据，直到最后。

```tcl
#!/usr/bin/tclsh

set c [read stdin 1]

while {$c != "q"} {

    puts -nonewline "$c"
    set c [read stdin 1]
}

```

该脚本从标准输入通道读取一个字符，然后将其写入标准输出，直到遇到`q`字符为止。

```tcl
set c [read stdin 1]

```

我们从标准输入通道（`stdin`）读取一个字符。

```tcl
while {$c != "q"} {

```

我们继续读取字符，直到按`q`为止。

## `gets`命令

`gets`命令从通道读取下一行，返回行中直到（但不包括）行尾字符的所有内容。

```tcl
#!/usr/bin/tclsh

puts -nonewline "Enter your name: "
flush stdout
set name [gets stdin]

puts "Hello $name"

```

该脚本要求用户输入，然后打印一条消息。

```tcl
puts -nonewline "Enter your name: "

```

`puts`命令用于将消息打印到终端。 `-nonewline`选项禁止换行符。

```tcl
flush stdout

```

Tcl 缓冲区在内部输出，因此用`puts`编写的字符可能不会立即出现在输出文件或设备上。 `flush`命令强制输出立即显示。

```tcl
set name [gets stdin]

```

`gets`命令从通道读取一条线。

```tcl
$ ./hello.tcl
Enter your name: Jan
Hello Jan

```

脚本的示例输出。

## `pwd`和`cd`命令

Tcl 具有`pwd`和`cd`命令，类似于 shell 命令。 `pwd`命令返回当前工作目录，`cd`命令用于更改工作目录。

```tcl
#!/usr/bin/tclsh

set dir [pwd]

puts $dir

cd ..

set dir [pwd]
puts $dir

```

在此脚本中，我们将打印当前工作目录。 然后，我们更改工作目录并再次打印工作目录。

```tcl
set dir [pwd]

```

`pwd`命令返回当前工作目录。

```tcl
cd ..

```

我们将工作目录更改为当前目录的父目录。 我们使用`cd`命令。

```tcl
$ ./cwd.tcl 
/home/janbodnar/prog/tcl/io
/home/janbodnar/prog/tcl

```

样本输出。

## `glob`命令

Tcl 具有`glob`命令，该命令返回与模式匹配的文件的名称。

```tcl
#!/usr/bin/tclsh

set files [glob *.tcl]

foreach file $files {

    puts $file
}

```

该脚本将所有带有`.tcl`扩展名的文件打印到控制台。

```tcl
set files [glob *.tcl]

```

`glob`命令返回与`*.tcl`模式匹配的文件列表。

```tcl
foreach file $files {

    puts $file
}

```

我们浏览文件列表，并将列表的每个项目打印到控制台。

```tcl
$ ./globcmd.tcl 
attributes.tcl
allfiles.tcl
printing.tcl
hello.tcl
read.tcl
files.tcl
globcmd.tcl
write2file.tcl
cwd.tcl
readfile.tcl
isfile.tcl
addnumbers.tcl

```

这是`globcmd.tcl`脚本的示例输出。

## 处理文件

`file`命令操纵文件名和属性。 它有很多选择。

```tcl
#!/usr/bin/tclsh

puts [file volumes]
[file mkdir new]

```

该脚本将打印系统的已安装卷，并创建一个新目录。

```tcl
puts [file volumes]

```

`file volumes`命令返回到系统上安装的卷的绝对路径。

```tcl
[file mkdir new]

```

`file mkdir`创建一个名为`new`的目录。

```tcl
$ ./voldir.tcl 
/
$ ls -d */
doc/  new/  tmp/

```

在 Linux 系统上，有一个已安装的卷-根目录。 `ls`命令确认`new`目录的创建。

在下面的代码示例中，我们将检查文件名是常规文件还是目录。

```tcl
#!/usr/bin/tclsh

set files [glob *]

foreach fl $files {

    if {[file isfile $fl]} {

        puts "$fl is a file"
    } elseif { [file isdirectory $fl]} {

        puts "$fl is a directory"
    }
}

```

我们遍历当前工作目录中的所有文件名，并打印它是文件还是目录。

```tcl
set files [glob *]

```

使用`glob`命令，我们创建当前目录的文件和目录名称的列表。

```tcl
if {[file isfile $fl]} {

```

如果有问题的文件名是文件，我们将执行`if`命令的主体。

```tcl
} elseif { [file isdirectory $fl]} {

```

`file isdirectory`命令确定文件名是否为目录。 请注意，在 Unix 上，目录是文件的特例。

`puts`命令可用于写入文件。

```tcl
#!/usr/bin/tclsh

set fp [open days w]

set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday}

puts $fp $days
close $fp

```

在脚本中，我们打开一个文件进行写入。 我们将一周中的几天写入文件。

```tcl
set fp [open days w]

```

我们打开一个名为`days`的文件进行写入。 `open`命令返回一个通道 ID。

```tcl
set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday}

```

该数据将被写入文件。

```tcl
puts $fp $days

```

我们使用`open`命令返回的通道 ID 写入文件。

```tcl
close $fp

```

我们关闭打开的频道。

```tcl
$ ./write2file.tcl
$ cat days 
Monday Tuesday Wednesday Thursday Friday Saturday Sunday

```

我们运行脚本并检查`days`文件的内容。

在下面的脚本中，我们将从文件中读取数据。

```tcl
$ cat languages 
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme

```

我们在目录中有一个简单的文件，称为语言。

```tcl
#!/usr/bin/tclsh

set fp [open languages r]
set data [read $fp]

puts -nonewline $data

close $fp

```

我们从提供的文件中读取数据，读取其内容，然后将数据打印到终端。

```tcl
set fp [open languages r]

```

我们通过以只读模式打开语言文件来创建频道。

```tcl
set data [read $fp]

```

如果我们没有为`read`命令提供第二个参数，它将从文件中读取所有数据，直到文件结尾。

```tcl
puts -nonewline $data

```

我们将数据打印到控制台。

```tcl
$ ./readfile.tcl 
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme

```

`readfile.tcl`脚本的示例运行。

`eof`命令检查所提供通道的行尾。

```tcl
#!/usr/bin/tclsh

set fp [open languages]

while {![eof $fp]} {
    puts [gets $fp]
}

close $fp

```

我们使用`eof`命令读取文件的内容。

```tcl
while {![eof $fp]} {
    puts [gets $fp]
}

```

循环继续进行，直到`eof`遇到文件结尾都返回`true`为止。 在体内，我们使用`gets`命令从文件中读取一行。

```tcl
$ ./readfile2.tcl 
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme

```

`readfile2.tcl`脚本的示例运行。

下一个脚本将执行一些其他文件操作。

```tcl
#!/usr/bin/tclsh

set fp [open newfile w]

puts $fp "this is new file"
flush $fp

file copy newfile newfile2
file delete newfile

close $fp

```

我们打开一个文件，并在其中写入一些文本。 文件被复制。 然后删除原始文件。

```tcl
file copy newfile newfile2

```

`file copy`命令复制文件。

```tcl
file delete newfile

```

原始文件用`file delete`命令删除。

在最后一个示例中，我们将使用文件属性。

```tcl
#!/usr/bin/tclsh

set files [glob *]

set mx 0

foreach fl $files {

    set len [string length $fl]

    if { $len > $mx} {

        set mx $len
    }
}

set fstr "%-$mx\s %-s"
puts [format $fstr Name Size]

set fstr "%-$mx\s %d bytes"
foreach fl $files {

    set size [file size $fl]

    puts [format $fstr $fl $size]

}

```

该脚本将创建两列。 在第一列中，我们有文件的名称。 在第二列中，我们显示文件的大小。

```tcl
foreach fl $files {

    set len [string length $fl]

    if { $len > $mx} {

        set mx $len
    }
}

```

在此循环中，我们找出了最长的文件名。 格式化输出列时将使用此格式。

```tcl
set fstr "%-$mx\s %-s"
puts [format $fstr Name Size]

```

在这里，我们打印列的标题。 要格式化数据，我们使用`format`命令。

```tcl
set fstr "%-$mx\s %d bytes"
foreach fl $files {

    set size [file size $fl]

    puts [format $fstr $fl $size]

}

```

我们遍历文件列表并打印每个文件名及其大小。 `file size`命令确定文件的大小。

```tcl
$ ./attributes.tcl 
Name           Size
attributes.tcl 337 bytes
newfile2       17 bytes
allfiles.tcl   75 bytes
printing.tcl   83 bytes
languages      51 bytes
hello.tcl      109 bytes
days           57 bytes
read.tcl       113 bytes
files.tcl      140 bytes
globcmd.tcl    82 bytes
write2file.tcl 134 bytes
doc            4096 bytes
cwd.tcl        76 bytes
tmp            4096 bytes
readfile.tcl   98 bytes
isfile.tcl     219 bytes

```

样品运行。

在本章中，我们介绍了 Tcl 中的输入/输出操作。