在Bash For循环中使用CD - 只获取相对路径

3
我有一个小脚本,用于将文件组织到目录中,现在我正在尝试在一个文件夹或多个文件夹上运行它。MasterDir/dir1、MasterDir/dir2等。
在MasterDir上运行以下代码会导致错误,因为$dir只是相对路径,但我无法弄清楚如何在这种情况下获取$dir的完整路径。
for dir in */; do
    echo $dir
    cd $dir
    cwd="$PWD"
    mkdir -p "VSI"
    mv -v *.vsi "$cwd/VSI"
    mv -v _*_ "$cwd/VSI"
done

查看这个 - Jeff Holt
absdir="$(readlink -f "$dir")"; pushd "$absdir" &>/dev/null; # cd through mv; popd &>/dev/null - David C. Rankin
3个回答

3
我建议使用括号将循环体放在子Shell中运行:
for dir in */; do
    (
        echo $dir
        cd $dir
        cwd="$PWD"
        mkdir -p "VSI"
        mv -v *.vsi "$cwd/VSI"
        mv -v _*_ "$cwd/VSI"
    )
done

由于这是在子shell中运行,cd命令不会影响执行脚本的父shell。


3
您遇到的问题是,当您第一次运行cd "$dir"之后,您所在的目录比使用for dir in */生成目录列表时低一个级别。因此,下一次调用cd "$dir"时会失败,因为您仍然在第一个子目录中cd,而您列表中下一个"$dir"在上一级目录中。
解决此问题的方法有几种。其中一种简单的方法是使用pushd而不是cd更改目录,这样可以使用popd返回到原始目录。(尽管在这种情况下,您可以简单地添加cd ..来返回到父目录,因为只有一层深)
使用pushd/popd,您可以执行以下操作:
for dir in */; do
    echo $dir
    pushd "$dir" &>/dev/null || {
        printf "error: failed to change to %s\n" "$dir" >&2
        continue
    }
    cwd="$PWD"
    mkdir -p "VSI" || {
        printf "error: failed to create %s\n" "$cwd/VSI" >&2
        continue
    }
    mv -v *.vsi "$cwd/VSI"
    mv -v _*_ "$cwd/VSI"
    popd &>/dev/null || {
        printf "error: failed to return to parent dir\n" >&2
        break
    }
done

注意: ||测试验证pushd,mkdir,popd的返回值,如果不能返回原始目录,则导致循环继续到下一个目录或中断循环。另请注意,&>/dev/null仅抑制pushd/popd的正常输出,而将输出重定向到>&2会将输出发送到stderr而不是stdout。)

正如评论中提到的,您总是可以使用readlink -f "$dir"生成"$dir"的绝对路径——尽管在这里并不是真正需要它。


直到现在我才第一次听说这个。非常感谢!真的解决了我在for循环中使用cd命令的问题! - kyrlon
1
很高兴能帮到你。即使是基本的POSIX shell中也有许多工具,几乎可以满足你在运行系统时的所有需求。祝你脚本编写顺利。 - David C. Rankin

1
这是我避免在shell脚本中使用cd的原因之一 -- 当你cd时,所有相对文件路径的意义都会改变,如果你不非常小心,就会出问题。另一个原因是cd可能会失败(例如由于权限错误),在这种情况下,最好要有一个错误检查和处理程序,否则会发生更奇怪的事情。
在我的看法中,直接使用显式路径来引用其他目录中的文件更安全。也就是说,不要使用cd somedir; mkdir -p "VSI",而是使用`mkdir -p "somedir/VSI"。以下是使用这种方法重写您的循环的代码:
for dir in */; do
    echo $dir
    mkdir -p "${dir}/VSI"
    mv -v "${dir}"/*.vsi "${dir}/VSI"
    mv -v "${dir}"/_*_ "${dir}/VSI"
done

注意:变量$dir的值将以斜杠结尾,因此使用例如${dir}/VSI将得到类似于“somedir//VSI”的结果。额外的斜杠是多余的,但不应该引起问题;我喜欢使用它来保持清晰度。如果这很烦人(例如在mv -v的输出中),可以省略额外的斜杠。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接