【问题标题】:Symlinking relative paths without expanding symlinks符号链接相对路径而不扩展符号链接
【发布时间】:2015-02-18 00:50:27
【问题描述】:

在 linux 上使用 C 语言工作,我有两个字符数组,其中包含相对于 cwd 的路径。假设他们是"foo/txt""bar/txt"。我想创建一个从第一个指向第二个的符号链接。问题是如果我去symlink("bar/txt", "foo/txt")foo/txt 将寻找不存在的foo/bar/txt。我可以将".." 添加到目标路径的开头,但我不知道源字符串中包含多少目录级别

使问题更加复杂的是,我的目标本身就是一个符号链接,我不想遍历它,如果我使用 realpath() 可能会发生这种情况。一旦按照我的意图创建了链接,目录结构可能如下所示(出于我的目的,生成的符号链接是相对的还是绝对的并不重要):

/somecwd/foo/txt -> ../bar/txt
/somecwd/bar/txt -> ../baz/txt
/somecww/baz/txt

有人知道我会怎么做吗?非常感谢任何帮助。

编辑:理想情况下,即使我给出的路径是绝对的,这也可以工作

【问题讨论】:

    标签: c linux symlink


    【解决方案1】:

    使用 symlink() 函数,BSD (Mac OS X) 手册页说:

     int symlink(const char *path1, const char *path2);
    

    描述

    符号链接path2 被创建到path1path2 是创建的文件的名称,path1 是用于创建符号链接的字符串)。任一名称都可以是任意路径名;这些文件不必在同一个文件系统上。

    请注意,您指定为path1 的内容将逐字用作符号链接的内容。因此,要准确地制作符号链接,您必须正确获取相对路径。也就是说,作为path1 传递的名称必须是相对于path2 的正确名称。

    换句话说,您无法完全避免映射名称的过程,随之而来的困难。

    我有一个 Perl 脚本 relpath,我从Convert absolute path into relative path given a current directory 的答案和comp.unix.shell 的一个相当古老的新闻组消息拼凑而成。将其中的一些转换为 C 并不超出人类的智慧。事实上,它肯定已经做过无数次了。困难在于找到代码。

    相对路径

    #!/usr/bin/env perl
    #
    # @(#)$Id: relpath.pl,v 1.4 2014/12/08 18:23:17 jleffler Exp $
    #
    # Usage:    relpath source target [...]
    #
    # Purpose:  Print relative path of target w.r.t. source
    #
    # Based loosely on code from:
    # http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-10/1256.html
    # Via: https://stackoverflow.com/questions/2564634
    
    use strict;
    use warnings;
    use File::Basename;
    use Cwd qw(realpath getcwd);
    
    if (scalar @ARGV < 2)
    {
        my $arg0 = basename($0, ".pl");
        die "Usage: $arg0 from to [...]\n"
    }
    
    my $pwd;
    my $verbose = 0;
    
    # Fettle filename so it is absolute.
    # Deals with '//', '/./' and '/../' notations, plus symlinks.
    # The realpath() function does the hard work if the path exists.
    # For non-existent paths, the code does a purely textual hack.
    sub resolve
    {
        my($name) = @_;
        my($path) = realpath($name);
        if (!defined $path)
        {
            # Path does not exist - do the best we can with lexical analysis
            # Assume Unix - not dealing with Windows.
            $path = $name;
            if ($name !~ m%^/%)
            {
                $pwd = getcwd if !defined $pwd;
                $path = "$pwd/$path";
            }
            $path =~ s%//+%/%g;     # Not UNC paths.
            $path =~ s%/$%%;        # No trailing /
            $path =~ s%/\./%/%g;    # No embedded /./
            # Try to eliminate /../abc/
            $path =~ s%/\.\./(?:[^/]+)(/|$)%$1%g;
            $path =~ s%/\.$%%;      # No trailing /.
            $path =~ s%^\./%%;      # No leading ./
            # What happens with . and / as inputs?
        }
        return($path);
    }
    
    sub print_result
    {
        my($source, $target, $relpath) = @_;
        if ($verbose)
        {
            print "source  = $ARGV[0]\n";
            print "target  = $ARGV[1]\n";
            print "relpath = $relpath\n";
        }
        else
        {
            print "$relpath\n";
        }
    }
    
    # Nasty!
    my($source) = resolve($ARGV[0]);
    my(@source) = split '/', $source;
    shift @ARGV;
    
    sub relpath
    {
        my($name) = @_;
        my($target) = resolve($name);
        print_result($source, $target, ".") if ($source eq $target);
    
        # Split!
        my(@target) = split '/', $target;
    
        my $count = scalar(@source);
           $count = scalar(@target) if (scalar(@target) < $count);
        my $relpath = "";
        my $i;
    
        # Both paths are absolute; Perl splits an empty field 0.
        for ($i = 1; $i < $count; $i++)
        {
            last if $source[$i] ne $target[$i];
        }
    
        for (my $s = $i; $s < scalar(@source); $s++)
        {
            $relpath = "$relpath/" if ($s > $i);
            $relpath = "$relpath..";
        }
        for (my $t = $i; $t < scalar(@target); $t++)
        {
            $relpath = "$relpath/" if ($relpath ne "");
            $relpath = "$relpath$target[$t]";
        }
    
        print_result($source, $target, $relpath);
    }
    
    foreach my $target (@ARGV)
    {
        relpath($target);
    }
    

    test.relpath

    注意:这需要relpath.pl 而不仅仅是relpath

    #!/bin/ksh
    #
    # @(#)$Id: test.relpath.sh,v 1.1 2010/04/25 15:19:20 jleffler Exp $
    #
    # Test relpath Perl script fairly exhaustively
    # BUG: should include expected answers!
    
    sed 's/#.*//;/^[    ]*$/d' <<! |
    
    /home/part1/part2 /home/part1/part3
    /home/part1/part2 /home/part4/part5
    /home/part1/part2 /work/part6/part7
    /home/part1       /work/part1/part2/part3/part4
    /home             /work/part2/part3
    /                 /work/part2/part3/part4
    
    /home/part1/part2 /home/part1/part2/part3/part4
    /home/part1/part2 /home/part1/part2/part3
    /home/part1/part2 /home/part1/part2
    /home/part1/part2 /home/part1
    /home/part1/part2 /home
    /home/part1/part2 /
    
    /home/part1/part2 /work
    /home/part1/part2 /work/part1
    /home/part1/part2 /work/part1/part2
    /home/part1/part2 /work/part1/part2/part3
    /home/part1/part2 /work/part1/part2/part3/part4
    
    home/part1/part2 home/part1/part3
    home/part1/part2 home/part4/part5
    home/part1/part2 work/part6/part7
    home/part1       work/part1/part2/part3/part4
    home             work/part2/part3
    .                work/part2/part3
    
    home/part1/part2 home/part1/part2/part3/part4
    home/part1/part2 home/part1/part2/part3
    home/part1/part2 home/part1/part2
    home/part1/part2 home/part1
    home/part1/part2 home
    home/part1/part2 .
    
    home/part1/part2 work
    home/part1/part2 work/part1
    home/part1/part2 work/part1/part2
    home/part1/part2 work/part1/part2/part3
    home/part1/part2 work/part1/part2/part3/part4
    
    !
    
    {
    echo "Relative paths (source, target, relative path)"
    while read source target
    do
        echo "$source $target $(${PERL:-perl} relpath.pl $source $target)"
    done |
    awk '{ printf("%-20s   %-30s   %s\n", $1, $2, $3); }'
    }
    

    示例输出

    请注意,如果/home/part1/part2 是目录或假定为目录,则第一个相对路径../part3 是正确的。此代码在解释输出时需要小心。请注意,symlink() 系统调用不要求 path1 名称引用现有文件或目录(但相比之下,path2 不得引用现有文件或目录,尽管只有叶元素不得存在;所有以前的目录都必须存在)。

    Relative paths (source, target, relative path)
    /home/part1/part2      /home/part1/part3                ../part3
    /home/part1/part2      /home/part4/part5                ../../part4/part5
    /home/part1/part2      /work/part6/part7                ../../../work/part6/part7
    /home/part1            /work/part1/part2/part3/part4    ../../work/part1/part2/part3/part4
    /home                  /work/part2/part3                ../work/part2/part3
    /                      /work/part2/part3/part4          work/part2/part3/part4
    /home/part1/part2      /home/part1/part2/part3/part4    part3/part4
    /home/part1/part2      /home/part1/part2/part3          part3
    /home/part1/part2      /home/part1/part2                .
    /home/part1/part2      /home/part1                      ..
    /home/part1/part2      /home                            ../..
    /home/part1/part2      /                                ../../..
    /home/part1/part2      /work                            ../../../work
    /home/part1/part2      /work/part1                      ../../../work/part1
    /home/part1/part2      /work/part1/part2                ../../../work/part1/part2
    /home/part1/part2      /work/part1/part2/part3          ../../../work/part1/part2/part3
    /home/part1/part2      /work/part1/part2/part3/part4    ../../../work/part1/part2/part3/part4
    home/part1/part2       home/part1/part3                 ../part3
    home/part1/part2       home/part4/part5                 ../../part4/part5
    home/part1/part2       work/part6/part7                 ../../../work/part6/part7
    home/part1             work/part1/part2/part3/part4     ../../work/part1/part2/part3/part4
    home                   work/part2/part3                 ../work/part2/part3
    .                      work/part2/part3                 work/part2/part3
    home/part1/part2       home/part1/part2/part3/part4     part3/part4
    home/part1/part2       home/part1/part2/part3           part3
    home/part1/part2       home/part1/part2                 .
    home/part1/part2       home/part1                       ..
    home/part1/part2       home                             ../..
    home/part1/part2       .                                ../../..
    home/part1/part2       work                             ../../../work
    home/part1/part2       work/part1                       ../../../work/part1
    home/part1/part2       work/part1/part2                 ../../../work/part1/part2
    home/part1/part2       work/part1/part2/part3           ../../../work/part1/part2/part3
    home/part1/part2       work/part1/part2/part3/part4     ../../../work/part1/part2/part3/part4
    

    【讨论】:

      【解决方案2】:

      作为一种快速解决方案(忽略双斜杠、.././),您可以计算源中的斜杠数量并在前面加上相同数量的 ../

      【讨论】:

        猜你喜欢
        • 2015-06-10
        • 1970-01-01
        • 2019-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-07
        • 2022-10-16
        相关资源
        最近更新 更多