如何移植使用“$@:2”?

Posted

技术标签:

【中文标题】如何移植使用“$@:2”?【英文标题】:How to portability use "$@:2"?如何移植使用“$@:2”? 【发布时间】:2019-11-11 07:35:09 【问题描述】:

在Allow for $@:2 syntax in variable assignment 上,他们说我不应该使用"$@:2",因为它会破坏不同的shell,我应该改用"$*:2"

但是使用"$*:2" 代替"$@:2" 是无稽之谈,因为使用"$@:2" 不等于"$*:2" 作为following example:

#!/bin/bash
check_args() 
  echo "\$#=$#"
  local counter=0
  for var in "$@"
  do
      counter=$((counter+1));
      printf "$counter. '$var', ";
  done
  printf "\\n\\n"


# setting arguments
set -- "space1 notspace" "space2 notspace" "lastargument"; counter=1
echo $counter': ---------------- "$*"'; counter=$((counter+1))
check_args "$*"

echo $counter': ---------------- "$*:2"'; counter=$((counter+1))
check_args "$*:2"

echo $counter': ---------------- "$@:2"'; counter=$((counter+1))
check_args "$@:2"

-->

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
1: ---------------- "$*"
$#=1
1. 'space1 notspace space2 notspace lastargument',

2: ---------------- "$*:2"
$#=1
1. 'space2 notspace lastargument',

3: ---------------- "$@:2"
$#=2
1. 'space2 notspace', 2. 'lastargument',

如果我不能使用"$@:2" (as they say),我可以用什么来代替?

这是Process all arguments except the first one (in a bash script) 的原始问题,他们将参数与空格放在一起的唯一答案是使用"$@:2"

【问题讨论】:

需要脚本的便携性如何?如果它是特定于 bash 的,可以吗? 如果不可能,最便携的,最好的。如果它是 POSIX 可移植的就好了。 【参考方案1】:

除非您点击链接,否则问题中的上下文不清楚。与the following recommendation from shellcheck.net有关:

local _help_text="$@:2"
                  ^––SC2124 Assigning an array to a string! Assign as array, or use * instead of @ to concatenate.

简短回答:不要将事物列表(如参数)分配给普通变量,而是使用数组。

长答案:通常,"$@:2" 将获取除第一个参数之外的所有参数,每个参数都被视为一个单独的项目(“单词”)。另一方面,"$*:2" 生成一个项目,该项目由除第一个参数外的所有参数粘在一起,由空格分隔(或 $IFS 的第一个字符)。

但在您分配给普通变量的特定情况下,该变量只能存储单个项目,因此var="$@:2" 将参数折叠为单个项目,但它的方式不如"$*:2"。为了避免这种情况,请使用能够存储多个项目的东西:数组。所以:

真的很糟糕:var="$@:2" 稍微不那么糟糕:var="$*:2" 好多了:arrayvar=("$@:2")(括号使它成为一个数组)

注意:要取回数组的元素,并将每个元素正确地视为单独的项目,请使用"$arrayvar[@]"。此外,并非所有 shell 都支持数组(值得注意的是,dash 不支持它们),所以如果你使用它们,你应该确保使用 bash shebang(#!/bin/bash#!/usr/bin/env bash)。如果您真的需要移植到其他 shell,事情会变得复杂得多

【讨论】:

【参考方案2】:

$@:2$*:2 都不是可移植的,许多 shell 会将两者视为无效语法而拒绝。如果你想处理除第一个参数之外的所有参数,你应该用 shift 去掉第一个参数。

first="$1"
shift
echo The arguments after the first are:
for x; do echo "$x"; done

此时,第一个参数在“$first”中,位置参数下移一个。

【讨论】:

【参考方案3】:

这演示了如何将所有 $@ 参数组合成一个变量,而无需破解 $@:1$@:2 (live example):

#!/bin/bash

function get_all_arguments_as_single_one_unquoted() 
    single_argument="$(printf "%s " "$@")";
    printf "unquoted arguments %s: '%s'\\n" "$#" "$single_argument";


function get_all_arguments_as_single_one_quoted() 
    single_argument="$1";
    printf "quoted arguments %s: '%s'\\n" "$#" "$single_argument";


function escape_arguments() 
    escaped_arguments="$(printf '%q ' "$@")";
    get_all_arguments_as_single_one_quoted "$escaped_arguments";
    get_all_arguments_as_single_one_unquoted $escaped_arguments;


set -- "first argument" "last argument";
escape_arguments "$@";

-->

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
quoted arguments 1: 'first\ argument last\ argument '
unquoted arguments 4: 'first\ argument last\ argument '

正如@William Pursell 回答指出的那样,如果您只想获得@:2 参数,您可以在"$@" 之前添加shift 调用

function escape_arguments() 
    shift;
    escaped_arguments="$(printf '%q ' "$@")";
    get_all_arguments_as_single_one_quoted "$escaped_arguments";
    get_all_arguments_as_single_one_unquoted $escaped_arguments;

-->

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
quoted arguments 1: 'last\ argument '
unquoted arguments 2: 'last\ argument '

【讨论】:

以上是关于如何移植使用“$@:2”?的主要内容,如果未能解决你的问题,请参考以下文章

如何将使用sysioctl.h的代码移植到MinGW gcc中?

使用 wmemset() 的代码的可移植性如何?

Linux内核移植入门

如何将 UML 模型移植到 Rational Software Architect 中

如何设置可移植的 persistence.xml 文件?

如何以可移植的方式在 C 中执行算术右移?