【问题标题】:Is the directory NOT writable目录不可写吗
【发布时间】:2013-03-24 16:44:59
【问题描述】:

谁能告诉我为什么总是说目录不可写,而绝对是?

$dnam="/home/bryan/renametest/CD" # 目录是否可写 错误=0 如果 [ ! -w $dnam] 然后 # 不可写。弹出错误并退出。 echo "目录 $dnam 不可写" 错误=1 菲

【问题讨论】:

  • 我发现您的代码有两个问题,并且无法重现您看到的错误。第一个问题$dnam=,不会飞。第二个问题:你应该更喜欢[[]]而不是[]。使用单一版本时,我看到“预期的二元运算符”错误。
  • 哦,抱歉……刚刚添加了 $dnam= 行,所以我可以展示它是什么。实际代码没有 $.实际上是:dnam=$(dirname $1)\"
  • 将 [] 更改为 [[]] 会导致 [[: not found

标签: linux bash shell testing writable


【解决方案1】:

您需要在$dnam 周围加上双引号——没有它们,它会被解释为两个单独的shell 词,“/home/bryan/renametest/C”和“D”,这会产生无效的测试表达式并因此失败。这应该有效:

if [ ! -w "$dnam" ]

@tink 对[[ ]] 的建议是进行此类测试的一种更简洁的方法,但仅在 bash(以及其他一些具有扩展语法的 shell)中可用。您得到[[: not found 的事实意味着您使用的是相当基本的shell,而不是bash。

【讨论】:

  • 啊哈哈!好的。谢谢你。这解决了那个错误。还有一个我要处理的。
  • 哎呀..不,我说得太早了。它仍然说它不可写。
  • @Gordon Davisson:但他的帖子被标记为 bash?
  • @tink:我很确定标签不准确。
  • @user2021539:从您对问题的评论来看,您似乎在$dnam 的值中嵌入了双引号——如果是这样,这将导致此问题(以及许多其他问题)问题。当 shell 解析命令行时,它首先解释(并删除)引号,然后 将变量替换为它们的值。如果变量的值包含引号,那么它们达到预期效果为时已晚,它们只会像任何其他字符一样被对待。在这种情况下,它将将该引号视为文件名的一部分以检查可写性,并且由于该名称没有任何内容,因此检查失败。
【解决方案2】:

我发现了多个问题:

  • 您在变量中使用了一个空格。这不是非法的,但在组合行中,您使用变量 unscaped 并生成以下命令:

    if [ ! -w /home/bryan/renametest/C D ]
    

    这不是一个有效的语法。解决此问题的最简单方法是将行更改为

    if [ ! -w "$dnam" ]
    
  • 下一个问题更糟糕:在我的系统上,help test 返回文本:

    -w FILE        True if the file is writable by you.
    

    也就是说,该命令不支持目录,只支持文件。如果要检查目录是否可写,则必须使用不同的命令

【讨论】:

  • -w 确实支持目录。
  • 检查其inode的写模式,但不检查目录是否可写。它不适用于:squashfs、只读挂载、isofs、acls 等...
【解决方案3】:

正如其他人所说,$dnam 变量需要双引号。原因如下:

[ ... ]test 命令的别名。如果你查看你的系统,你会看到一个名为/bin/[/bin/usr/[ 的文件。在某些系统上,这是指向/bin/test/bin/usr/test 的硬链接。 if 语句执行if 之后的内容,如果该命令返回零退出状态,if 语句将执行then 子句。否则,如果有 else 子句,则会改为执行。

为了进行布尔测试,Unix 包含了test 命令,所以你可以这样做:

if test -d "$directory"
then
    echo "Directory $directory exists!"
fi

后来,/bin/[ 被添加为语法糖。这与上面的相同:

if [ -d "$directory" ]
then
    echo "Directory $directory exists!"
fi

现在,[test 都是内置命令,但它们是 *still 命令。这意味着shell会插入命令然后执行它。

尝试执行以下操作:

$ set -xv    # Turns on shell debugging
$ dnam="/home/bryan/renametest/C D"
dnam="/home/bryan/renametest/C D"
+ dnam='/home/bryan/renametest/C D'
$ test -d $dnam 
test -d $dnam
+ test -d /home/bryan/renametest/C D
$ echo $?
echo $?
+ echo 1
1
$ test -d "$dnam"   # Now with quotes
test -d $dnam
+ test -d "/home/bryan/renametest/C D"
$ echo $?
echo $?
+ echo 0
0
$ set +xv     # Turn off the debuggin

每个命令都回显两次。第一次写入,第二次插入行后。作为插值的一部分,shell 在空白处分割参数。如您所见,test 命令正在测试/home/bryan/renamtest/C 的存在,它不存在因此不可写。我真的很惊讶 test 命令没有打印错误消息,因为您向它传递了一个额外的参数。

在第二次尝试中,您添加了引号。这些引号防止 shell 将您的参数拆分为空间并将目录名称保留为单个参数。

由于[ ... ]是一个命令,所以必须考虑到shell对变量的插值等问题。而且,如果您不是非常小心,最终可能会出现错误。

更糟糕的是,有时[ ... ] 可能有效,有时可能无效。如果您的目录名称不包含空格,它将按预期工作。想象一下,您正在编写一个程序,并且您对其进行测试并且一切正常,因为您尝试过的所有目录都没有空格。然后,有人使用您的程序,但在目录中有空间。 if 语句中的此类问题存在大量 shell 脚本错误。

这就是 Bash 引入 [[ ... ]] 测试的原因。 [[ 不是命令而是语句。这意味着外壳不会直接对结果进行插值。相反,参数被解析,然后进行任何插值。因此,这是可行的:

dnam="/home/bryan/renametest/C D"   # No "$" in front of the variable!

# Is the directory writable
if [[ ! -w $dnam ]]      # No quotation marks needed!
then
    # Not writable. Pop the error and exit.
    echo "Directory $dnam is not writable"
    err=1
fi

使用[[ ... ]] 测试几乎总是比使用[ ... ] 测试更好,所以继续养成这个习惯吧。

还有一个小错误,你有:

$dnam="/home/bryan/renametest/C D"

这由 shell 插入,所以设置的变量是 $dnam 的值。如果$dnam 恰好等于“foo”,你会这样做:

foo="/home/bryan/renametest/C D"

不是你想要的。

您想在设置变量时关闭$

dnam="/home/bryan/renametest/C D"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-06
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 2013-09-22
    • 2012-08-29
    • 2017-05-03
    相关资源
    最近更新 更多