(如何)是否可以使用标准 shell 命令 and/or Unix utils 仅向那些已经拥有 r 权限的人添加 x 权限?

(How) is it possible to add the x permission only to those who already have the r permission using standard shell commands and/or Unix utils?

我想编写一个 shell 脚本,它将对多个文件进行操作,并且(除其他外)将使它们可执行。

问题:文件有不同的权限——特别是,有些文件对所有用户都可读,有些只对所有者可读——我想为那些有读取权限的人添加执行权限(也就是说,如果所有人都可以读取文件,那么所有人都应该能够执行它,但是如果只有所有者可以读取它,那么只有所有者才能执行它)。

我知道如何通过使用任意数量的编程语言编写一个小程序并使用几种不同的方法(从对每个代理的权限的天真、直接的条件到巧妙地使用掩码)来实现这一点和权限位的移动),但我想在“纯”shell 脚本中实现这一点,只使用标准 shell 命令和 Unix utils。

更新: 目前有两个答案,这两个都提出了有趣的技术(使用 zsh 的 glob 限定符,并使用 find) 这似乎与我需要的密切相关,但略微忽略了问题的重点,因为我面临的挑战是 而不是 找到要处理的文件:脚本的用户将告诉它要处理哪些文件。相反,挑战在于,给定一个特定文件,例如,根据是否已设置 o+r 权限来决定是否对其调用 chmod o+x

这可能不是最有效的方法,但我真的很喜欢 find 命令,因为它直观 API。

以下命令查找当前目录 (.) 中组或所有用户可读的所有文件和目录:

find . -perm /go=r

此命令查找所有者可读但组或所有用户不可读的所有文件和目录:

find . -perm /u=r ! -perm /go=r

找到要处理的文件后,您可以添加一个 -exec 参数来对每个匹配项执行命令。 例如,以下命令将在所有文件和目录上设置所有者的可执行位,这些文件和目录只能由所有者读取:

find . -perm /u=r ! -perm /go=r -exec chmod u+x {} \;

该行的末尾很奇怪,但完全按照所示输入它应该可以。

并且此命令将在组和其他人可读的所有文件和目录上为“组”和“其他人”设置可执行位:

find . -perm /go=r -exec chmod go+x {} \;

所有这些命令都在当前目录中递归搜索。您可以通过将 . 替换为另一个路径来指定任意其他目录。

您还可以使用 -maxdepth N 指定搜索的深度。

编辑: 如果要处理单个指定文件,可以使用以下脚本。它通过使用 stat 命令检查文件的权限,其中 returns 格式类似于 ls -l 显示的权限,例如 -rw-r--r--.

myfile="/tmp/abc"

permissions="$(stat -c %A $myfile)"
if [[ "$permissions" =~ ^....r.....$ ]] || [[ "$permissions" =~ ^.......r..$ ]]; then
    # file is readable by group and/or others
    # set executable bit for owner, group and others
    chmod a+x "$myfile"
elif [[ "$permissions" =~ ^.r........$ ]]; then
    # file is note readable by group or others
    # Set executable bit for owner only
    chmod u+x "$myfile"
fi

此脚本适用于 bash 和 zsh。

如果使用 zsh 作为您的 shell,您可以利用 glob qualifiers 将匹配限制为具有和不具有特定权限位的文件:

# chmod all plain files (.) that are world-readable (R) but not world-executable (^X)
zsh$ chmod o+x *(.R^X)
# files that are owner-readable but not owner-executable
zsh$ chmod u+x *(.r^x)
# And groups
zsh$ chmod g+x *(.A^E)

您还可以执行 *(.X^R) 之类的操作来获取 world-executable 而不是 world-readable 的文件,或者其他具有权限的组合(在 ^ 之前)而没有它(在 ^ 之后)。还有一个 ffspec 限定符,可以更好地控制权限位匹配;有关详细信息,请参阅文档。

要获取目录树中的所有文件而不仅仅是当前目录,请使用 **/* 而不是 *

到目前为止提供的答案提供了有用的想法,但是,由于其中 none 回答了所问的问题,我特此发布我自己的问题的答案。

此答案提供了三种不同的解决方案,每种都基于先前发布的答案中建议的不同方法;还有其他未涵盖的方法(我只提到,没有显示实际代码,将权限作为八进制数获取的技巧,然后 - 使用某种执行 arithmetic/bit-wise 计算的机制 - 并将其用444得到r位,右移2位得到对应的x位,与原权限或,结果设置为新权限。

以下所有解决方案均假定处理文件 "$FILE".

解决方案 1 – if将当前权限的模式设置为文本

这可能是最直接的方法:

permissions=$(stat -f %SA "$FILE")
if [[ $permissions =~ .r........ ]]; then chmod u+x "$FILE"; fi
if [[ $permissions =~ ....r..... ]]; then chmod g+x "$FILE"; fi
if [[ $permissions =~ .......r.. ]]; then chmod o+x "$FILE"; fi

解决方案 2 – 使用 find,利用 -perm-exec 选项

此方法使用 find 不是查找多个文件,而是查找给定文件(如果它具有特定权限)或找不到任何文件(否则),并设置匹配找到文件时所需的权限(否则不执行任何操作):

find "$FILE" -perm -u=r -exec chmod u+x {} \;
find "$FILE" -perm -g=r -exec chmod g+x {} \;
find "$FILE" -perm -o=r -exec chmod o+x {} \;

这三种情况可以很容易地组合成一个 for 循环:

for w in u g o; find "$FILE" -perm -$w=r -exec chmod $w+x {} \;; done

解决方案 3(仅 zsh)——使用 glob 限定符

此方法仅在 "$FILE" 上运行每个相关的 chmod 命令,以防它已经匹配相应的权限。注意添加 || : 以避免在不匹配的情况下出现错误,即如果文件没有相应的权限(另请注意 zsh 在这种情况下仍会发出 no matches found 警告):

chmod u+x "$FILE"(r) || :
chmod g+x "$FILE"(A) || :
chmod o+x "$FILE"(R) || :