使用 HDInsight 进行脚本操作开发Script action development with HDInsight

了解如何使用 Bash 脚本自定义 HDInsight 群集。Learn how to customize your HDInsight cluster using Bash scripts. 在创建群集期间和之后,可以通过脚本操作自定义 HDInsight。Script actions are a way to customize HDInsight during or after cluster creation.

什么是脚本操作What are script actions

脚本操作是 Azure 在群集节点上运行的以进行配置更改或安装软件的 Bash 脚本。Script actions are Bash scripts that Azure runs on the cluster nodes to make configuration changes or install software. 脚本操作以 root 身份执行,提供对群集节点的完全访问权限。A script action is executed as root, and provides full access rights to the cluster nodes.

可通过以下方法应用脚本操作:Script actions can be applied through the following methods:

使用此方法来应用脚本...Use this method to apply a script... 在创建群集期间...During cluster creation... 在运行中的群集上...On a running cluster...
Azure 门户Azure portal
Azure PowerShellAzure PowerShell
Azure 经典 CLIAzure Classic CLI  
HDInsight .NET SDKHDInsight .NET SDK
Azure Resource Manager 模板Azure Resource Manager Template  

有关使用这些方法来应用脚本操作的详细信息,请参阅使用脚本操作自定义 HDInsight 群集For more information on using these methods to apply script actions, see Customize HDInsight clusters using script actions.

脚本开发最佳实践Best practices for script development

在针对 HDInsight 群集开发自定义脚本时,有些最佳做法要铭记于心:When you develop a custom script for an HDInsight cluster, there are several best practices to keep in mind:

Important

脚本操作必须在 60 分钟内完成,否则进程将失败。Script actions must complete within 60 minutes or the process fails. 在节点预配期间,脚本将与其他安装和配置进程一同运行。During node provisioning, the script runs concurrently with other setup and configuration processes. 争用 CPU 时间和网络带宽等资源可能导致完成脚本所需的时间要长于在开发环境中所需的时间。Competition for resources such as CPU time or network bandwidth may cause the script to take longer to finish than it does in your development environment.

选择目标 Apache Hadoop 版本Target the Apache Hadoop version

不同版本的 HDInsight 有不同版本的 Hadoop 服务和已安装的组件。Different versions of HDInsight have different versions of Hadoop services and components installed. 如果脚本需要特定版本的服务或组件,应该只在包含所需组件的 HDInsight 版本中使用该脚本。If your script expects a specific version of a service or component, you should only use the script with the version of HDInsight that includes the required components. 可以使用 HDInsight 组件版本控制文档来查找 HDInsight 随附组件版本的相关信息。You can find information on component versions included with HDInsight using the HDInsight component versioning document.

检查操作系统版本Checking the operating system version

不同版本的 HDInsight 依赖于特定版本的 Ubuntu。Different versions of HDInsight rely on specific versions of Ubuntu. 脚本必须查看的 OS 版本之间可能存在差异。There may be differences between OS versions that you must check for in your script. 例如,可能需要安装与 Ubuntu 版本相关的二进制文件。For example, you may need to install a binary that is tied to the version of Ubuntu.

若要检查 OS 版本,请使用 lsb_releaseTo check the OS version, use lsb_release. 例如,以下脚本演示如何根据 OS 版本引用特定的 tar 文件:For example, the following script demonstrates how to reference a specific tar file depending on the OS version:

OS_VERSION=$(lsb_release -sr)
if [[ $OS_VERSION == 14* ]]; then
    echo "OS version is $OS_VERSION. Using hue-binaries-14-04."
    HUE_TARFILE=hue-binaries-14-04.tgz
elif [[ $OS_VERSION == 16* ]]; then
    echo "OS version is $OS_VERSION. Using hue-binaries-16-04."
    HUE_TARFILE=hue-binaries-16-04.tgz
fi

确定针对的操作系统版本Target the operating system version

HDInsight 取决于 Ubuntu Linux 发行版。HDInsight is based on the Ubuntu Linux distribution. 不同版本的 HDInsight 依赖不同版本的 Ubuntu,这可能会改变脚本的行为方式。Different versions of HDInsight rely on different versions of Ubuntu, which may change how your script behaves. 例如,HDInsight 3.4 及更低版本基于使用 Upstart 的 Ubuntu 版本。For example, HDInsight 3.4 and earlier are based on Ubuntu versions that use Upstart. 版本 3.5 和更高版本取决于使用 Systemd 的 Ubuntu 16.04。Versions 3.5 and greater are based on Ubuntu 16.04, which uses Systemd. Systemd 和 Upstart 采用不同的命令,因此,编写的脚本应能与这两者配合使用。Systemd and Upstart rely on different commands, so your script should be written to work with both.

HDInsight 3.4 和 3.5 的另一个重要区别在于 JAVA_HOME 现在能够指向 Java 8。Another important difference between HDInsight 3.4 and 3.5 is that JAVA_HOME now points to Java 8. 以下代码演示如何确定脚本是在 Ubuntu 14 还是 16 上运行:The following code demonstrates how to determine if the script is running on Ubuntu 14 or 16:

OS_VERSION=$(lsb_release -sr)
if [[ $OS_VERSION == 14* ]]; then
    echo "OS version is $OS_VERSION. Using hue-binaries-14-04."
    HUE_TARFILE=hue-binaries-14-04.tgz
elif [[ $OS_VERSION == 16* ]]; then
    echo "OS version is $OS_VERSION. Using hue-binaries-16-04."
    HUE_TARFILE=hue-binaries-16-04.tgz
fi
...
if [[ $OS_VERSION == 16* ]]; then
    echo "Using systemd configuration"
    systemctl daemon-reload
    systemctl stop webwasb.service    
    systemctl start webwasb.service
else
    echo "Using upstart configuration"
    initctl reload-configuration
    stop webwasb
    start webwasb
fi
...
if [[ $OS_VERSION == 14* ]]; then
    export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
elif [[ $OS_VERSION == 16* ]]; then
    export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
fi

可以在 https://hdiconfigactions.blob.core.windows.net/linuxhueconfigactionv02/install-hue-uber-v02.sh 中找到包含这些代码片段的完整脚本。You can find the full script that contains these snippets at https://hdiconfigactions.blob.core.windows.net/linuxhueconfigactionv02/install-hue-uber-v02.sh.

有关 HDInsight 所使用的 Ubuntu 版本,请参阅 HDInsight 组件版本文档。For the version of Ubuntu that is used by HDInsight, see the HDInsight component version document.

若要了解 Systemd 和 Upstart 之间的差异,请参阅 Upstart 用户的 SystemdTo understand the differences between Systemd and Upstart, see Systemd for Upstart users.

提供指向脚本资源的可靠链接Provide stable links to script resources

在群集的整个生存期内,脚本和关联的资源必须保持可用。The script and associated resources must remain available throughout the lifetime of the cluster. 如果在缩放操作期间将新节点添加到了群集,需要用到这些资源。These resources are required if new nodes are added to the cluster during scaling operations.

最佳做法是下载订阅上 Azure 存储帐户中的所有内容并将其存档。The best practice is to download and archive everything in an Azure Storage account on your subscription.

Important

使用的存储帐户必须是群集的默认存储帐户,或其他任何存储帐户的公共只读容器。The storage account used must be the default storage account for the cluster or a public, read-only container on any other storage account.

例如,Microsoft 提供的示例存储在 https://hdiconfigactions.blob.core.windows.net/ 存储帐户中。For example, the samples provided by Microsoft are stored in the https://hdiconfigactions.blob.core.windows.net/ storage account. 此位置是 HDInsight 团队维护的一个公共只读容器。This location is a public, read-only container maintained by the HDInsight team.

使用预编译的资源Use pre-compiled resources

若要减少运行脚本所花费的时间,请避免使用从源代码编译资源的操作。To reduce the time it takes to run the script, avoid operations that compile resources from source code. 例如,对资源进行预编译并将其存储在与 HDInsight 相同的数据中心的 Azure 存储帐户 blob 中。For example, pre-compile resources and store them in an Azure Storage account blob in the same data center as HDInsight.

确保群集自定义脚本是幂等的Ensure that the cluster customization script is idempotent

脚本必须是幂等的。Scripts must be idempotent. 如果脚本多次运行,则每次都应将群集返回到相同的状态。If the script runs multiple times, it should return the cluster to the same state every time.

例如,修改配置文件的脚本如果运行多次,不应添加重复的项。For example, a script that modifies configuration files should not add duplicate entries if ran multiple times.

确保群集体系结构的高可用性Ensure high availability of the cluster architecture

基于 Linux 的 HDInsight 群集提供在群集中保持活动状态的两个头节点,而脚本操作会同时在这两个节点上运行。Linux-based HDInsight clusters provide two head nodes that are active within the cluster, and script actions run on both nodes. 如果安装的组件只应使用一个头节点,请不要在两个头节点上安装组件。If the components you install expect only one head node, do not install the components on both head nodes.

Important

作为 HDInsight 一部分提供的服务旨在根据需要在两个头节点之间进行故障转移。Services provided as part of HDInsight are designed to fail over between the two head nodes as needed. 此功能未扩展到通过脚本操作安装的自定义组件。This functionality is not extended to custom components installed through script actions. 如果需要为自定义组件提供高可用性,必须实现自己的故障转移机制。If you need high availability for custom components, you must implement your own failover mechanism.

配置自定义组件以使用 Azure Blob 存储Configure the custom components to use Azure Blob storage

在群集上安装的组件可能具有使用 Apache Hadoop 分布式文件系统 (HDFS) 存储的默认配置。Components that you install on the cluster might have a default configuration that uses Apache Hadoop Distributed File System (HDFS) storage. HDInsight 使用 Azure 存储作为默认存储。HDInsight uses either Azure Storage as the default storage. 两者可以提供与 HDFS 兼容的文件系统,即使删除了群集,也能保存数据。Both provide an HDFS compatible file system that persists data even if the cluster is deleted. 可能需要将安装的组件配置为使用 WASB 或 ADL,而不是 HDFS。You may need to configure components you install to use WASB or ADL instead of HDFS.

对于大多数操作,不需要指定文件系统。For most operations, you do not need to specify the file system. 例如,以下脚本将 hadoop-common.jar 文件从本地文件系统复制到群集存储:For example, the following copies the hadoop-common.jar file from the local file system to cluster storage:

hdfs dfs -put /usr/hdp/current/hadoop-client/hadoop-common.jar /example/jars/

在此示例中,hdfs 命令以透明方式使用默认群集存储。In this example, the hdfs command transparently uses the default cluster storage. 对于某些操作,可能需要指定 URI。For some operations, you may need to specify the URI. 例如,为 Data Lake Storage Gen2 指定 abfs:///example/jars,或者为 Azure 存储指定 wasb:///example/jarsFor example, abfs:///example/jars for Data Lake Storage Gen2 or wasb:///example/jars for Azure Storage.

将信息写入 STDOUT 和 STDERRWrite information to STDOUT and STDERR

HDInsight 会记录已写入 STDOUT 和 STDERR 的脚本输出。HDInsight logs script output that is written to STDOUT and STDERR. 可以使用 Ambari Web UI 查看这些信息。You can view this information using the Ambari web UI.

Note

只有在成功创建群集之后,才能使用 Apache Ambari。Apache Ambari is only available if the cluster is successfully created. 如果在群集创建期间使用脚本操作但创建失败,请参阅使用脚本操作自定义 HDInsight 群集的故障排除部分,了解访问已记录信息的其他方式。If you use a script action during cluster creation, and creation fails, see the troubleshooting section Customize HDInsight clusters using script action for other ways of accessing logged information.

大多数实用工具和安装包已将信息写入 STDOUT 和 STDERR,不过你可能需要添加更多日志记录。Most utilities and installation packages already write information to STDOUT and STDERR, however you may want to add additional logging. 若要将文本发送到 STDOUT,请使用 echoTo send text to STDOUT, use echo. 例如:For example:

echo "Getting ready to install Foo"

默认情况下,echo 会将字符串发送到 STDOUT。By default, echo sends the string to STDOUT. 若要将它定向到 STDERR,请在 echo 的前面添加 >&2To direct it to STDERR, add >&2 before echo. 例如:For example:

>&2 echo "An error occurred installing Foo"

这会将写入 STDOUT 的信息改为重定向到 STDERR (2)。This redirects information written to STDOUT to STDERR (2) instead. 有关 IO 重定向的详细信息,请参阅 https://www.tldp.org/LDP/abs/html/io-redirection.htmlFor more information on IO redirection, see https://www.tldp.org/LDP/abs/html/io-redirection.html.

若要详细了解如何查看脚本操作记录的信息,请参阅使用脚本操作自定义 HDInsight 群集For more information on viewing information logged by script actions, see Customize HDInsight clusters using script action

将文件另存为包含 LF 行尾的 ASCIISave files as ASCII with LF line endings

应将 Bash 脚本存储为 ASCII 格式,该格式以 LF 作为行尾。Bash scripts should be stored as ASCII format, with lines terminated by LF. 存储为 UTF-8 的文件或使用 CRLF 作为行尾的文件可能发生故障,并返回以下错误:Files that are stored as UTF-8, or use CRLF as the line ending may fail with the following error:

$'\r': command not found
line 1: #!/usr/bin/env: No such file or directory

使用重试逻辑从暂时性错误中恢复Use retry logic to recover from transient errors

下载文件、使用 apt-get 安装包或者执行通过 Internet 传输数据的其他操作时,可能会由于暂时性网络错误而失败。When downloading files, installing packages using apt-get, or other actions that transmit data over the internet, the action may fail because of transient networking errors. 例如,与你通信的远程资源可能正在故障转移到备份节点。For example, the remote resource you're communicating with may be in the process of failing over to a backup node.

若要使脚本能够从暂时性错误中恢复,可以实现重试逻辑。To make your script resilient to transient errors, you can implement retry logic. 以下函数演示如何实现重试逻辑。The following function demonstrates how to implement retry logic. 它在失败之前重试操作三次。It retries the operation three times before failing.

#retry
MAXATTEMPTS=3

retry() {
    local -r CMD="$@"
    local -i ATTMEPTNUM=1
    local -i RETRYINTERVAL=2

    until $CMD
    do
        if (( ATTMEPTNUM == MAXATTEMPTS ))
        then
                echo "Attempt $ATTMEPTNUM failed. no more attempts left."
                return 1
        else
                echo "Attempt $ATTMEPTNUM failed! Retrying in $RETRYINTERVAL seconds..."
                sleep $(( RETRYINTERVAL ))
                ATTMEPTNUM=$ATTMEPTNUM+1
        fi
    done
}

以下示例演示如何使用此函数。The following examples demonstrate how to use this function.

retry ls -ltr foo

retry wget -O ./tmpfile.sh https://hdiconfigactions.blob.core.windows.net/linuxhueconfigactionv02/install-hue-uber-v02.sh

自定义脚本的帮助器方法Helper methods for custom scripts

脚本操作帮助器方法是可以在编写自定义脚本时使用的实用工具。Script action helper methods are utilities that you can use while writing custom scripts. 这些方法包含在 https://hdiconfigactions.blob.core.windows.net/linuxconfigactionmodulev01/HDInsightUtilities-v01.sh 脚本中。These methods are contained in thehttps://hdiconfigactions.blob.core.windows.net/linuxconfigactionmodulev01/HDInsightUtilities-v01.sh script. 使用以下命令下载这些方法并在脚本中使用:Use the following to download and use them as part of your script:

# Import the helper method module.
wget -O /tmp/HDInsightUtilities-v01.sh -q https://hdiconfigactions.blob.core.windows.net/linuxconfigactionmodulev01/HDInsightUtilities-v01.sh && source /tmp/HDInsightUtilities-v01.sh && rm -f /tmp/HDInsightUtilities-v01.sh

可在脚本中使用以下帮助器:The following helpers available for use in your script:

帮助器用法Helper usage 说明Description
download_file SOURCEURL DESTFILEPATH [OVERWRITE] 将文件从源 URI 下载到指定的文件路径。Downloads a file from the source URI to the specified file path. 默认情况下,它不会覆盖现有的文件。By default, it does not overwrite an existing file.
untar_file TARFILE DESTDIR 将 tar 文件解压缩(使用 -xf)到目标目录。Extracts a tar file (using -xf) to the destination directory.
test_is_headnode 如果在群集头节点上运行,则返回 1;否则返回 0。If ran on a cluster head node, return 1; otherwise, 0.
test_is_datanode 如果当前节点是数据(辅助角色)节点,则返回 1;否则返回 0。If the current node is a data (worker) node, return a 1; otherwise, 0.
test_is_first_datanode 如果当前节点是第一个数据(辅助角色)节点(名为 workernode0),则返回 1;否则返回 0。If the current node is the first data (worker) node (named workernode0) return a 1; otherwise, 0.
get_headnodes 返回群集中头节点的完全限定域名。Return the fully qualified domain name of the headnodes in the cluster. 名称以逗号分隔。Names are comma delimited. 出错时返回空字符串。An empty string is returned on error.
get_primary_headnode 获取主头节点的完全限定域名。Gets the fully qualified domain name of the primary headnode. 出错时返回空字符串。An empty string is returned on error.
get_secondary_headnode 获取辅助头节点的完全限定域名。Gets the fully qualified domain name of the secondary headnode. 出错时返回空字符串。An empty string is returned on error.
get_primary_headnode_number 获取主头节点的数字后缀。Gets the numeric suffix of the primary headnode. 出错时返回空字符串。An empty string is returned on error.
get_secondary_headnode_number 获取辅助头节点的数字后缀。Gets the numeric suffix of the secondary headnode. 出错时返回空字符串。An empty string is returned on error.

常见使用模式Common usage patterns

本部分提供有关实现你在编写自己的自定义脚本时可能遇到的一些常见使用模式的指导。This section provides guidance on implementing some of the common usage patterns that you might run into while writing your own custom script.

将参数传递给脚本Passing parameters to a script

在某些情况下,脚本可能需要参数。In some cases, your script may require parameters. 例如,使用 Ambari REST API 时,可能需要群集的管理员密码。For example, you may need the admin password for the cluster when using the Ambari REST API.

传递给脚本的参数称为“位置参数” ,将分配到 $1 作为第一个参数,分配到 $2 作为第二个参数,依此类推。Parameters passed to the script are known as positional parameters, and are assigned to $1 for the first parameter, $2 for the second, and so-on. $0 包含脚本本身的名称。$0 contains the name of the script itself.

作为参数传递给脚本的值应括在单引号 (') 中。Values passed to the script as parameters should be enclosed by single quotes ('). 这样可以确保将传递的值视为文本。Doing so ensures that the passed value is treated as a literal.

设置环境变量Setting environment variables

以下语句设置环境变量:Setting an environment variable is performed by the following statement:

VARIABLENAME=value

其中,VARIABLENAME 是变量的名称。Where VARIABLENAME is the name of the variable. 若要访问变量,请使用 $VARIABLENAMETo access the variable, use $VARIABLENAME. 例如,若要将位置参数提供的值指定为名为 PASSWORD 的环境变量,请使用以下语句:For example, to assign a value provided by a positional parameter as an environment variable named PASSWORD, you would use the following statement:

PASSWORD=$1

对信息进行后续访问时可以使用 $PASSWORDSubsequent access to the information could then use $PASSWORD.

在脚本中设置的环境变量只在脚本范围内存在。Environment variables set within the script only exist within the scope of the script. 在某些情况下,可能需要添加系统范围的环境变量,这些变量在脚本完成之后仍会存在。In some cases, you may need to add system-wide environment variables that will persist after the script has finished. 若要添加系统范围的环境变量,请将变量添加到 /etc/environmentTo add system-wide environment variables, add the variable to /etc/environment. 例如,以下语句添加 HADOOP_CONF_DIRFor example, the following statement adds HADOOP_CONF_DIR:

echo "HADOOP_CONF_DIR=/etc/hadoop/conf" | sudo tee -a /etc/environment

访问存储自定义脚本的位置Access to locations where the custom scripts are stored

用于自定义群集的脚本需要存储在以下位置之一:Scripts used to customize a cluster needs to be stored in one of the following locations:

  • 与群集关联的 Azure 存储帐户An Azure Storage account that is associated with the cluster.

  • 与群集关联的 其他存储帐户An additional storage account associated with the cluster.

  • 可公开读取的 URIA publicly readable URI. 例如,在 OneDrive、Dropbox 或其他文件托管服务中存储的数据的 URL。For example, a URL to data stored on OneDrive, Dropbox, or other file hosting service.

  • 与 HDInsight 群集关联的 Azure Data Lake Storage 帐户An Azure Data Lake Storage account that is associated with the HDInsight cluster. 有关将 Azure Data Lake Storage 与 HDInsight 配合使用的详细信息,请参阅快速入门:在 HDInsight 中设置群集For more information on using Azure Data Lake Storage with HDInsight, see Quickstart: Set up clusters in HDInsight.

    Note

    用于访问 Data Lake Storage 的服务主体 HDInsight 必须具有对脚本的读取访问权限。The service principal HDInsight uses to access Data Lake Storage must have read access to the script.

脚本使用的资源也必须公开提供。Resources used by the script must also be publicly available.

与在 Azure 网络中一样,在 Azure 存储帐户或 Azure Data Lake Storage 中存储文件可提供快速访问。Storing the files in an Azure Storage account or Azure Data Lake Storage provides fast access, as both within the Azure network.

Note

用于引用脚本的 URI 格式因所使用的服务而异。The URI format used to reference the script differs depending on the service being used. 对于与 HDInsight 群集关联的存储帐户,请使用 wasb://wasbs://For storage accounts associated with the HDInsight cluster, use wasb:// or wasbs://. 对于可公开读取的 URI,请使用 http://https://For publicly readable URIs, use http:// or https://.

有关部署脚本操作的清单Checklist for deploying a script action

下面是在准备部署脚本时执行的步骤:Here are the steps take when preparing to deploy a script:

  • 将包含自定义脚本的文件放置在群集节点在部署期间可访问的位置中。Put the files that contain the custom scripts in a place that is accessible by the cluster nodes during deployment. 例如,群集的默认存储。For example, the default storage for the cluster. 还可以将文件存储在可公开读取的托管服务中。Files can also be stored in publicly readable hosting services.
  • 验证脚本是幂等的。Verify that the script is idempotent. 这样,便可以在同一个节点上执行脚本多次。Doing so allows the script to be executed multiple times on the same node.
  • 使用临时文件目录 /tmp 来保存脚本使用的下载文件,并在执行脚本后将其清除。Use a temporary file directory /tmp to keep the downloaded files used by the scripts and then clean them up after scripts have executed.
  • 如果更改了 OS 级别设置或 Hadoop 服务配置文件,可能需要重新启动 HDInsight 服务。If OS-level settings or Hadoop service configuration files are changed, you may want to restart HDInsight services.

如何运行脚本操作How to run a script action

可以使用以下方法通过脚本操作自定义 HDInsight 群集:You can use script actions to customize HDInsight clusters using the following methods:

  • Azure 门户Azure portal
  • Azure PowerShellAzure PowerShell
  • Azure Resource Manager 模板Azure Resource Manager templates
  • HDInsight .NET SDK。The HDInsight .NET SDK.

有关使用每种方法的详细信息,请参阅如何使用脚本操作For more information on using each method, see How to use script action.

自定义脚本示例Custom script samples

Microsoft 提供了在 HDInsight 群集上安装组件的示例脚本。Microsoft provides sample scripts to install components on an HDInsight cluster. 请参阅可充当示例脚本操作的在 HDInsight 群集上安装并使用 Hue 一文。See Install and use Hue on HDInsight clusters as an example script action.

故障排除Troubleshooting

使用开发的脚本时可能会遇到以下错误:The following are errors you may come across when using scripts you've developed:

错误:$'\r': command not foundError: $'\r': command not found. 有时后面会接着出现“ syntax error: unexpected end of file”。Sometimes followed by syntax error: unexpected end of file.

原因: 此错误的原因是脚本中以 CRLF 作为行尾。Cause: This error is caused when the lines in a script end with CRLF. Unix 系统只允许使用 LF 作为行尾。Unix systems expect only LF as the line ending.

此问题最常出现于 Windows 环境中编写的脚本,因为 CRLF 是 Windows 上许多文本编辑器中常见的行尾符号。This problem most often occurs when the script is authored on a Windows environment, as CRLF is a common line ending for many text editors on Windows.

解决方法:如果文本编辑器提供了选项,请选择 Unix 格式或以 LF 作为行尾。Resolution: If it is an option in your text editor, select Unix format or LF for the line ending. 也可以在 Unix 系统上使用以下命令,将 CRLF 更改为 LF:You may also use the following commands on a Unix system to change the CRLF to an LF:

Note

以下命令大致相当于将 CRLF 行尾更改为 LF。The following commands are roughly equivalent in that they should change the CRLF line endings to LF. 根据系统中提供的实用工具选择一种解决方法。Select one based on the utilities available on your system.

命令Command 注释Notes
unix2dos -b INFILE 原始文件以 .BAK 扩展名备份The original file is backed up with a .BAK extension
tr -d '\r' < INFILE > OUTFILE OUTFILE 包含只带 LF 行尾的版本OUTFILE contains a version with only LF endings
perl -pi -e 's/\r\n/\n/g' INFILE 直接修改文件Modifies the file directly
sed 's/$'"/`echo \\\r`/" INFILE > OUTFILE OUTFILE 包含只带 LF 行尾的版本。OUTFILE contains a version with only LF endings.

错误:line 1: #!/usr/bin/env: No such file or directoryError: line 1: #!/usr/bin/env: No such file or directory.

原因: 将脚本另存为包含字节顺序标记 (BOM) 的 UTF-8 时会发生此错误。Cause: This error occurs when the script was saved as UTF-8 with a Byte Order Mark (BOM).

解决方法:将文件另存为 ASCII,或者不带 BOM 的 UTF-8。Resolution: Save the file either as ASCII, or as UTF-8 without a BOM. 也可以在 Linux 或 Unix 系统上使用以下命令来创建不带 BOM 的文件:You may also use the following command on a Linux or Unix system to create a file without the BOM:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}{print}' INFILE > OUTFILE

INFILE 替换为包含 BOM 的文件。Replace INFILE with the file containing the BOM. OUTFILE 应是新文件名,该文件包含不带 BOM 的脚本。OUTFILE should be a new file name, which contains the script without the BOM.

后续步骤Next steps