检查文件是否可执行


78

我想知道在不执行程序的情况下检查程序是否可以通过bash执行的最简单方法是什么?它至少应检查该文件是否具有执行权限,并且与当前系统具有相同的体系结构(例如,不是Windows可执行文件或其他不受支持的体系结构,如果系统是32位,则不是64位,...)。


我认为ls -la [filename]或stat [filename]
ControlAltDel 2012年

ls -la和stat都不提供有关受支持体系结构的信息,这实际上是我最感兴趣的问题的一部分。我遇到了一个错误,因为尚未为我的体系结构编译可执行文件,因此我想创建一个避免将来发生这种情况的脚本。
bob 2012年

2
@bob:您是否尝试检查file这些文件上run的输出?
FatalError 2012年

@FatalError如何解析文件输出以检查可执行文件是否与我的体系结构兼容?
bob 2012年

Answers:


110

看一下各种测试运算符(这是针对测试命令本身的,但是内置的BASH和TCSH测试或多或少是相同的)。

您会注意到-x FILEFILE存在并且授予了执行(或搜索)权限

BASH,Bourne,Ksh,Zsh脚本

if [[ -x "$file" ]]
then
    echo "File '$file' is executable"
else
    echo "File '$file' is not executable or found"
fi

TCSH或CSH脚本:

if ( -x "$file" ) then
    echo "File '$file' is executable"
else
    echo "File '$file' is not executable or found"
endif

要确定文件类型,请尝试使用file命令。您可以解析输出以查看确切的文件类型。单词'o警告:有时file将返回多行。这是在Mac上发生的情况:

$ file /bin/ls    
/bin/ls: Mach-O universal binary with 2 architectures
/bin/ls (for architecture x86_64):  Mach-O 64-bit executable x86_64
/bin/ls (for architecture i386):    Mach-O executable i386

file命令根据操作系统返回不同的输出。但是,该词executable将出现在可执行程序中,并且通常该体系结构也会出现。

将以上内容与我在Linux机器上获得的内容进行比较:

$ file /bin/ls
/bin/ls: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), stripped

和一个Solaris盒:

$ file /bin/ls
/bin/ls:        ELF 32-bit MSB executable SPARC Version 1, dynamically linked, stripped

在所有三个,你会看到这个词executable和架构(x86-64i386,或SPARC32-bit)。


附录

非常感谢,这似乎是正确的选择。在将其标记为我的答案之前,您能否请我指导我对“文件”执行哪种脚本外壳检查(即,哪种解析),以检查我是否可以执行程序?如果从总体上来说这样的测试太难了,我至少要检查它是linux可执行文件还是osX(Mach-O)

在我的头顶上,您可以在BASH中执行以下操作:

if [ -x "$file" ] && file "$file" | grep -q "Mach-O"
then
    echo "This is an executable Mac file"
elif [ -x "$file" ] && file "$file" | grep -q "GNU/Linux"
then
    echo "This is an executable Linux File"
elif [ -x "$file" ] && file "$file" | grep q "shell script"
then
    echo "This is an executable Shell Script"
elif [ -x "$file" ]
then
    echo "This file is merely marked executable, but what type is a mystery"
else
    echo "This file isn't even marked as being executable"
fi

基本上,我正在运行测试,然后如果测试成功,则对file命令的输出执行grep 。该grep -q方法不输出任何输出,而是使用grep的退出代码来查看是否找到了该字符串。如果您的系统不运行grep -q,则可以尝试grep "regex" > /dev/null 2>&1

同样,该file命令的输出可能因系统而异,因此您必须验证它们是否可以在您的系统上工作。另外,我正在检查可执行文件位。如果文件是二进制可执行文件,但可执行文件位未打开,我将说它不是可执行文件。这可能不是您想要的。


伯恩壳有[[或只是[
glenn jackman 2012年

2
@glennjackman Bourne外壳仅具有[实际上是一个外部命令,而不是外壳的内置命令。它通常位于/bin目录中。这是test命令的别名。
David W.

非常感谢您,这似乎是正确的选择。在将其标记为我的答案之前,您能否请我指导我对“文件”执行哪种脚本外壳检查(即,哪种解析),以检查我是否可以执行程序?如果从总体上来说这样的测试太困难了,我至少要检查它是linux可执行文件还是osX(Mach-O)。
bob 2012年

@bob请参阅我的答案的附录。
David W.

谢谢,我将其标记为我的答案。我的想法是完全不同的:我在想可以将该可执行文件与系统上已经存在的可执行文件进行比较,以查看它们是否具有相同的体系结构。例如,将其与文件“ / bin / ls”进行比较。您认为这是个好主意吗?
bob 2012年

16

似乎没有人注意到-x运算符与目录没有不同。

因此,要精确检查可执行文件,您可以使用 [[ -f SomeFile && -x SomeFile ]]


5

似乎也没有人注意到符号链接上的-x运算符。到常规文件(未归类为可执行文件)的符号链接(链)未通过测试。


5

测试文件,目录和符号链接

此处给出的解决方案在目录或符号链接(或两者)上均无效。在Linux上,您可以使用以下命令测试文件,目录和符号链接:

if [[ -f "$file" && -x $(realpath "$file") ]]; then .... fi

在OS X上,您应该能够使用homebrew和use安装coreutils grealpath

定义isexec功能

为了方便起见,您可以定义一个函数:

isexec() {
    if [[ -f "$1" && -x $(realpath "$1") ]]; then
        true;
    else
        false;
    fi;
}

或者简单地

isexec() { [[ -f "$1" && -x $(realpath "$1") ]]; }

然后,您可以使用以下方法进行测试:

if `isexec "$file"`; then ... fi

你不是要return true代替echo true吗?
jan6

@ jan6是的,谢谢。我可能使用该版本进行测试,却忘记更改它。
pyrocrasty

同样,oneliner在结束括号之前缺少分号(至少bash希望这样,]]; }但不确定您如何错过该分号),尽管它不会改变功能,但您甚至不需要在if语句中打勾:)(我猜想它使突出显示不同,如果返回状态正确,则没有其他区别)
jan6

@ jan6:谢谢,我只是在上次编辑时添加了它。我没想到要检查bash(zsh不需要分号)。
pyrocrasty

另外,关于shell差异,在第一个版本中使用“ return true”在zsh上不能正常工作(尽管在bash上可以)。不过,仅将其替换为“ true”即可。
pyrocrasty

0

要测试文件本身是否ACL_EXECUTE在任何权限集(用户,组,其他)中都设置了位,而无论它位于何处,即:e。即使在带有noexec选项的 tmpfs上 ,也可以使用来获取许可权字符串,然后检查其是否至少包含单个“ x”字母:stat -c '%A'

if [[ "$(stat -c '%A' 'my_exec_file')" == *'x'* ]] ; then
    echo 'Has executable permission for someone'
fi

可以对比较的右手部分进行修改,以适应更具体的情况,例如*x*x*x*,检查将文件放在通过exec选项安装的卷上时,是否所有类型的用户都应能够执行该文件 。


0

这可能不是很明显,但是需要一定的时间来测试可执行文件以在没有外部shell进程的情况下适当地调用它:

function tkl_is_file_os_exec()
{
  [[ ! -x "$1" ]] && return 255

  local exec_header_bytes
  case "$OSTYPE" in
    cygwin* | msys* | mingw*)
      # CAUTION:
      #   The bash version 3.2+ might require a file path together with the extension,
      #   otherwise will throw the error: `bash: ...: No such file or directory`.
      #   So we make a guess to avoid the error.
      #
      {
        read -r -n 4 exec_header_bytes 2> /dev/null < "$1" ||
        {
          [[ -x "${1%.exe}.exe" ]] && read -r -n 4 exec_header_bytes 2> /dev/null < "${1%.exe}.exe"
        } ||
        {
          [[ -x "${1%.com}.com" ]] && read -r -n 4 exec_header_bytes 2> /dev/null < "${1%.com}.com"
        }
      } &&
      if [[ "${exec_header_bytes:0:3}" == $'MZ\x90' ]]; then
        # $'MZ\x90\00' for bash version 3.2.42+
        # $'MZ\x90\03' for bash version 4.0+
        [[ "${exec_header_bytes:3:1}" == $'\x00' || "${exec_header_bytes:3:1}" == $'\x03' ]] && return 0
      fi
    ;;
    *)
      read -r -n 4 exec_header_bytes < "$1"
      [[ "$exec_header_bytes" == $'\x7fELF' ]] && return 0
    ;;
  esac

  return 1
}

# executes script in the shell process in case of a shell script, otherwise executes as usual
function tkl_exec_inproc()
{
  if tkl_is_file_os_exec "$1"; then
    "$@"
  else
    . "$@"
  fi
  return $?
}

myscript.sh

#!/bin/bash

echo 123

return 123

在Cygwin中

> tkl_exec_inproc /cygdrive/c/Windows/system32/cmd.exe /c 'echo 123'
123
> tkl_exec_inproc /cygdrive/c/Windows/system32/chcp.com 65001
Active code page: 65001
> tkl_exec_inproc ./myscript.sh
123
> echo $?
123

在Linux中

> tkl_exec_inproc /bin/bash -c 'echo 123'
123
> tkl_exec_inproc ./myscript.sh
123
> echo $?
123
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.