Tcl中的两级符号链接追踪函数

Tcl中的两级符号链接追踪函数,tcl,Tcl,我尝试的结果是编写一个等价于以下shell的Tcl函数: get_real_home () { dirname $(ls -l $(ls -l $(which "$1") | awk '{print $NF}') | awk '{print $NF'}) } 简言之,这将为我提供包含实际二进制文件的目录的名称,当我给它一个由管理的程序的名称时,该程序将按照符号链接(通常在/usr/bin中)运行到/etc/alternations/中的另一个符号链接,该符号链接指向当前使用的替代程序的

我尝试的结果是编写一个等价于以下shell的Tcl函数:

get_real_home () {
    dirname $(ls -l $(ls -l $(which "$1") | awk '{print $NF}') | awk '{print $NF'})
}
简言之,这将为我提供包含实际二进制文件的目录的名称,当我给它一个由管理的程序的名称时,该程序将按照符号链接(通常在
/usr/bin
中)运行到
/etc/alternations/
中的另一个符号链接,该符号链接指向当前使用的替代程序的可执行文件(或任何内容)。例如:

$ get_real_home java
/usr/lib/jvm/java-6-openjdk-amd64/jre/bin
我之所以想这样做,是因为我使用的是Tcl,它的“母语”是Tcl,来管理许多编译器、解释器和库的环境设置(主要是
PATH
LD\u LIBRARY\u PATH
)。这个实用程序实际上是集群上的标准

特别是对于Java(在有许多替代方案的情况下),可以通过环境模块“知道”当前Debian替代方案指向的位置,将环境(例如,
Java_HOME
)设置为当前Debian替代方案的正确值。为此,上面的符号链接追逐器非常方便

当然,我可以将我已经拥有的(上面)粘贴到一个shell脚本中,并在环境模块中从Tcl调用它:一个实用但不雅观的解决方案。我更喜欢更好的“本机”Tcl解决方案,但由于我对Tcl完全不了解,我很难做到这一点,尽管它看起来应该是微不足道的


我相信这对了解Tcl的人来说是微不足道的,但那不是我:(文件规范化
命令使这几乎不费吹灰之力

set javaBinDir [file dirname [file normalize {*}[auto_execok java]]]
(对于
java
程序,它相当于
exec,它
;对于shell内置程序,它更复杂。它返回一个列表,在本例中是一个单例。我正在扩展它,以防有一个带有s的目录名称中的配速,或一些不平衡的大括号。不太可能…)


如果目标本身是一个链接,则需要做更多的工作

set java [file normalize [lindex [auto_execok java] 0]]
while {[file type $java] eq "link"} {
    # Ought to check for link loops...
    set java [file normalize [file join [file dirname $java] [file readlink $java]]]
}
puts "java really resolves to $java"

file normalize
不会自动为您执行此操作,因为您可能希望引用链接本身,而不是它所引用的内容。幸运的是,
file join
在显示相对和绝对组件时做了正确的操作;当我在(模拟)中尝试它时,它似乎起到了作用示例。

使用
文件规范化
命令几乎可以轻松完成此操作

set javaBinDir [file dirname [file normalize {*}[auto_execok java]]]
(对于
java
程序,它相当于
exec,它
;对于shell内置程序,它更复杂。它返回一个列表,在本例中是一个单例。我正在扩展它,以防有一个带有s的目录名称中的配速,或一些不平衡的大括号。不太可能…)


如果目标本身是一个链接,则需要做更多的工作

set java [file normalize [lindex [auto_execok java] 0]]
while {[file type $java] eq "link"} {
    # Ought to check for link loops...
    set java [file normalize [file join [file dirname $java] [file readlink $java]]]
}
puts "java really resolves to $java"

file normalize
不会自动为您执行此操作,因为您可能希望引用链接本身,而不是它所引用的内容。幸运的是,
file join
在显示相对和绝对组件时做了正确的操作;当我在(模拟)中尝试它时,它似乎起到了作用示例。

使用
文件规范化
命令几乎可以轻松完成此操作

set javaBinDir [file dirname [file normalize {*}[auto_execok java]]]
(对于
java
程序,它相当于
exec,它
;对于shell内置程序,它更复杂。它返回一个列表,在本例中是一个单例。我正在扩展它,以防有一个带有s的目录名称中的配速,或一些不平衡的大括号。不太可能…)


如果目标本身是一个链接,则需要做更多的工作

set java [file normalize [lindex [auto_execok java] 0]]
while {[file type $java] eq "link"} {
    # Ought to check for link loops...
    set java [file normalize [file join [file dirname $java] [file readlink $java]]]
}
puts "java really resolves to $java"

file normalize
不会自动为您执行此操作,因为您可能希望引用链接本身,而不是它所引用的内容。幸运的是,
file join
在显示相对和绝对组件时做了正确的操作;当我在(模拟)中尝试它时,它似乎起到了作用示例。

使用
文件规范化
命令几乎可以轻松完成此操作

set javaBinDir [file dirname [file normalize {*}[auto_execok java]]]
(对于
java
程序,它相当于
exec,它
;对于shell内置程序,它更复杂。它返回一个列表,在本例中是一个单例。我正在扩展它,以防有一个带有s的目录名称中的配速,或一些不平衡的大括号。不太可能…)


如果目标本身是一个链接,则需要做更多的工作

set java [file normalize [lindex [auto_execok java] 0]]
while {[file type $java] eq "link"} {
    # Ought to check for link loops...
    set java [file normalize [file join [file dirname $java] [file readlink $java]]]
}
puts "java really resolves to $java"

file normalize
不会自动为您执行此操作,因为您可能希望引用链接本身,而不是它所引用的内容。幸运的是,
file join
在显示相对和绝对组件时做了正确的操作;当我在(模拟)中尝试它时,它似乎起到了作用示例。

因此,几个小时后我回答了我自己的问题。虽然很详细,但很有效。下面给出了作为命令调用时我想要的答案,尽管它不会像那样使用

#!/usr/bin/env tclsh

# Equivalent to shell "which", returning the first occurence of its
# argument, cmd, on the PATH:
proc which {cmd} {
    foreach dir [split $::env(PATH) :] {
        set fqpn $dir/$cmd
        if { [file exists $fqpn] } {
            return $fqpn
        }
    }
}

# True if 'path' exists and is a symbolic link:
proc is_link {path} {
    return [file exists $path] && [string equal [file type $path] link]
}

# Chases a symbolic link until it resolves to a file that
# isn't a symlink:
proc chase {link} {
    set max_depth 10 ; # Sanity check
    set i 0
    while { [is_link $link] && $i < $max_depth } {
        set link [file link $link]
        incr i
    }
    if { $i >= $max_depth } {
        return -code error "maximum link depth ($max_depth) exceeded"
    }
    return $link
}

# Returns the "true home" of its argument, a command:
proc get_real_home {cmd} {
    set utgt [chase [which $cmd]]    ; # Ultimate target
    set home [file dirname $utgt]    ; # Directory containing target
    if { [string equal bin [file tail $home]] } {
        set home [file dirname $home]
    }
    return $home
}

# Not worried about command-line argument validation because
# none of the above will be used in a command context
set cmd  [lindex $argv 0]       ; # Command
set home [get_real_home $cmd]   ; # Ultimate home
puts "$cmd -> $home"
!/usr/bin/env tclsh
#相当于shell“which”,返回其
#参数cmd,位于路径上:
proc哪个{cmd}{
foreach dir[split$::env(路径):]{
设置fqpn$dir/$cmd
如果{[文件存在$fqpn]}{
返回$fqpn
}
}
}
#如果“路径”存在并且是符号链接,则为True:
proc是链接{path}{
返回[文件存在$path]&&[字符串相等[文件类型$path]链接]
}
#追逐符号链接,直到它解析为
#不是符号链接:
proc chase{link}{
设置最大深度10;#健全性检查
设置i 0
而{[is_link$link]