路径独立shebangs

Ame*_*ina 21 shell cron zsh path

我有一个脚本,希望能够在两台机器上运行。这两台机器从同一个 git 存储库中获取脚本的副本。脚本需要使用正确的解释器(例如zsh)运行。

不幸的是,这两个 envzsh住在本地和远程计算机不同的位置:

远程机器

$ which env
/bin/env

$ which zsh
/some/long/path/to/the/right/zsh
Run Code Online (Sandbox Code Playgroud)

本地机

$ which env
/usr/bin/env

$which zsh
/usr/local/bin/zsh
Run Code Online (Sandbox Code Playgroud)

如何设置shebang以便像/path/to/script.sh往常一样运行脚本使用Zsh可用的PATH

And*_*ese 22

你不能直接通过 shebang 解决这个问题,因为 shebang 是纯静态的。如果这个 LCM 不是 zsh,你可以做的是在 shebang 中有一些“最不常见的乘数”(从 shell 的角度来看),然后用正确的 shell 重新执行你的脚本。换句话说:让您的脚本由在所有系统上找到的 shell 执行,测试一个zsh-only 功能,如果测试结果为 false,则让脚本exec带有zsh,测试将成功,您只需继续。

zsh例如, 中的一个独特特征是$ZSH_VERSION变量的存在:

#!/bin/sh -

[ -z "$ZSH_VERSION" ] && exec zsh - "$0" ${1+"$@"}

# zsh-specific stuff following here
echo "$ZSH_VERSION"
Run Code Online (Sandbox Code Playgroud)

在这个简单的例子中,脚本首先由/bin/sh(所有 80 年代后类 Unix 系统都理解#!并有一个/bin/sh,Bourne 或 POSIX 但我们的语法兼容两者)。如果$ZSH_VERSION没有设置,脚本exec的本身通过zsh。如果$ZSH_VERSION设置了(相应的脚本已经运行zsh),则简单地跳过测试。瞧。

这只会在zsh根本不存在的$PATH情况下失败。

编辑:要确保,你只有exec一个zsh在平时的地方,你可以使用类似

for sh in /bin/zsh \
          /usr/bin/zsh \
          /usr/local/bin/zsh; do
    [ -x "$sh" ] && exec "$sh" - "$0" ${1+"$@"}
done
Run Code Online (Sandbox Code Playgroud)

这可以避免您意外地exec在您的东西中出现$PATH不是zsh您所期望的东西。


slm*_*slm 7

多年来,我一直使用类似的方法来处理需要运行脚本的系统上 Bash 的不同位置。

Bash/Zsh/等。

#!/bin/sh

# Determines which OS and then reruns this script with approp. shell interp.
LIN_BASH="/bin/sh";
SOL_BASH="/packages/utilities/bin/sun5/bash";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  $SOL_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
elif [ $OS_TYPE = "Linux" ]; then
  $LIN_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
else
  echo "UNKNOWN OS_TYPE, $OS_TYPE";
  exit 1;
fi
exit 0;

### BEGIN

...script goes here...
Run Code Online (Sandbox Code Playgroud)

以上可以很容易地适应各种口译员。关键是这个脚本最初作为 Bourne shell 运行。然后它再次递归调用自己,但### BEGIN使用sed.

珀尔

这是 Perl 的类似技巧:

#!/bin/sh

LIN_PERL="/usr/bin/perl";
SOL_PERL="/packages/perl/bin/perl";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  eval 'exec $SOL_PERL -x -S $0 ${1+"$@"}';
elif [ $OS_TYPE = "Linux" ]; then
  eval 'exec $LIN_PERL -x -S $0 ${1+"$@"}';
else
  echo "$OS_TYPE: UNSUPORRTED OS/PLATFORM";
  exit 0;
fi
exit 0;

#!perl

...perl script goes here...
Run Code Online (Sandbox Code Playgroud)

该方法利用 Perl 的能力,当给定一个文件运行时,将跳过该行之前的所有行来解析所述文件#! perl