【问题标题】:BASH - Refer to parameters using variableBASH - 使用变量引用参数
【发布时间】:2013-07-04 10:56:29
【问题描述】:

我正在尝试制作一个将二进制数计算为十进制的小脚本。它的工作原理是这样的:

-获取“1”或“0”位作为单独的参数。例如“./bin2dec 1 0 0 0 1 1 1”。

-对于每个参数位:如果是'1',则乘以对应的2的幂(在上述情况下,最左边的'1'将是64),然后将其添加到'sum'变量中.

这是代码(在指出的地方是错误的):

    #!/bin/bash

    p=$((2**($#-1)))  #Finds the power of two for the first parameter.
    sum=0  #The sum variable, to be used for adding the powers of two in it.


    for (( i=1; i<=$#; i++ ))  #Counts all the way from 1, to the total number of parameters.
    do

            if [ $i -eq 1 ]  # *THIS IS THE WRONG POINT* If parameter content equals '1'... 
            then
                    sum=$(($sum+$p))  #...add the current power of two in 'sum'.
            fi

    p=$(($p/2))  #Divides the power with 2, so to be used on the next parameter.
    done


    echo $sum  #When finished show the 'sum' content, which is supposed to be the decimal equivalent.

我的问题在注意点(第 10 行,包括空白行)。在那里,我试图检查每个参数的内容是否等于 1。我怎样才能使用变量来做到这一点?

例如,$1 是第一个参数,$2 是第二个参数,以此类推。我希望它像 $i 一样,其中 'i' 是每次增加一的变量,以便与下一个参数匹配。

除其他外,我试过这个:'$(echo "$"$i)' 但没有用。

我知道我的问题很复杂,我努力尽可能地把它说清楚。 有什么帮助吗?

【问题讨论】:

  • 附带说明:bash 可以像这样进行转换 --> $ printf '%d\n' "$((2#1000111))" 返回 71。
  • @fedorqui 或只是echo $((2#1000111))
  • Ikr,我只是想要一个使用我上面那样的脚本。并找出我们是否以及如何使用变量来引用参数。

标签: bash variables loops for-loop parameters


【解决方案1】:

这个怎么样?

#!/usr/bin/bash

res=0

while [[ $# -gt 0 ]]
do
    res=$(($res * 2 + $1))
    shift
done

echo $res

$ ./bin2dec 1 0 0 1
9

shift 是一个命令,它将传递给脚本的每个参数向左移动,将其值减 1,因此 $2 的值在移位后现在为 $1。实际上,您也可以使用带移位的数字来指示变量移位的距离,因此shift 就像shift 1,而shift 2 将给$1 提供过去在$3 的值。

【讨论】:

  • shift 是一个命令,它将传递给脚本的每个参数向左移动,将其值减 1,因此 $2 的值现在位于 $1 之后 @987654333 @.
  • 这个 Shift 命令确实很有趣,还有你用来计算小数的方式。虽然这仍然没有回答我原来的问题。
【解决方案2】:

这个怎么样?

#!/bin/bash

p=$((2**$#))
while(($#)); do
   ((sum+=$1*(p/=2)))
   shift
done
echo "$sum"

或者这个(往另一个方向):

#!/bin/bash

while(($#)); do
    ((sum=2*sum+$1))
    shift
done
echo "$sum"

请注意,没有错误检查。

请考虑 fedorqui 的评论:使用echo $((2# binary number )) 将二进制转换为十进制。

另外请注意,如果您提供太多参数,这将很容易溢出。


如果您想要不溢出的东西,请考虑使用dc

dc <<< "2i101010p"

(使用2i 将输入基数设置为2,并将101010 放入堆栈并printing 它)。

或者如果你更喜欢bc

bc <<< "ibase=2;101010"

请注意,这些需要将二进制数作为一个参数输入,而不是像您想要的那样将所有数字分开。如果你真的需要将所有数字分开,你还可以使用这些有趣的方法:

纯 Bash。

#!/bin/bash

echo $((2#$(printf '%s' "$@")))

带直流电。

#!/bin/bash

dc <<< "2i$(printf '%s' "$@")p"

与 bc.

#!/bin/bash

bc <<< "ibase=2;$(printf '%s' "$@")"

滥用 IFS,使用 dc。

#!/bin/bash

( IFS=; echo "2i$*p" ) | dc

滥用 IFS,与 bc.

#!/bin/bash

( IFS=; echo "ibase=2;$*" ) | bc

所以我们仍然没有回答您的问题(您对 OP 的评论中的那个):并找出我们是否以及如何使用变量来引用参数。使用 间接扩展 的 Bash。您可以在Shell Parameter Expansion section of the Bash reference manual 中了解它。最好的办法是在终端中向您展示:

$ a="hello"
$ b=a
$ echo "$b"
a
$ echo "${!b}"
hello

整洁,嗯?与位置参数类似:

$ set param1 param2 param3
$ echo "$1, $2, $3"
param1, param2, param3
$ i=2
$ echo "$i"
2
$ echo "${!i}"
param2

希望这会有所帮助!

【讨论】:

  • 感谢您的分析和有用的回复。这肯定花费了很多时间和精力!
【解决方案3】:

要通过名称间接引用变量,请使用${!name}

i=2
echo "${!i} is now equivalent to $2"

【讨论】:

    【解决方案4】:

    也试试这个:

    #!/bin/bash - 
    ( IFS=''; echo $((2#$*)) )
    

    【讨论】:

    • 这将全局设置IFS。可能很危险。请参阅我的答案以获得更安全的替代方案!
    • 在 gniourf_gniourf 发表评论后更改。
    猜你喜欢
    • 2016-08-03
    • 2014-01-01
    • 1970-01-01
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-03
    相关资源
    最近更新 更多