5

我需要在 shell 脚本中创建一个堆栈,以便推送要在循环中处理的值。第一个要求是这必须以可移植的方式实现,因为我想将脚本用作可移植的安装程序(至少在类 Unix 操作系统之间)。第二个要求是它需要能够在循环内部进行更改,因为新信息可以在循环处理条目时以递归方式出现。第三个要求是每个条目我有不止一行的信息(这个多是一个固定的数字,如果不是的话可以根据第一行的信息来计算)。

我的尝试是使用堆栈文件:

#!/bin/sh

echo "First entry" > stack.txt
echo "More info for the first entry" >> stack.txt
echo "Even more info for the first entry" >> stack.txt

while read ENTRY < stack.txt; do
    INFO2=`tail -n +2 stack.txt | head -n 1`
    INFO3=`tail -n +3 stack.txt | head -n 1`

    tail -n "+4" stack.txt > stack2.txt

    # Process the entry...

    # When we have to push something:
    echo "New entry" > stack.txt
    echo "Info 2" >> stack.txt
    echo "Info 3" >> stack.txt

    # Finally, rebuild stack
    cat stack2.txt >> stack.txt
done

这很有效,只是感觉不对。有没有更“hacky”的方法来做到这一点?

提前感谢您的帮助!

4

5 回答 5

2

在此处查看“示例 27-7。空数组和空元素”部分。具体来说,评论说,上面是“推”,“流行”是:

http://tldp.org/LDP/abs/html/arrays.html

如果您想为每个元素编码多行,我建议您使用 base64 或 JSON 对这些行进行编码。您还可以使用 url 编码或使用 echo 转义字符。

由于您需要使用数组,因此您可以在 sh 中使用以下数组示例:

http://www.linuxquestions.org/questions/linux-general-1/how-to-use-array-in-sh-shell-644142/

于 2012-10-04T02:13:23.593 回答
2

与使用文件相比,使用目录并将每个项目存储在自己的文件中似乎更容易。例如:

#!/bin/sh 

count=0
push() { echo "$*" > $stackdir/item.$((++count)); }
pop() { 
    if test $count = 0; then
        : > $stackdir/data
    else
        mv $stackdir/item.$((count--)) $stackdir/data
    fi
}
trap 'rm -rf $stackdir' 0
stackdir=$( mktemp -d ${TMPDIR-/tmp}/stack.XXXX )

push some data
push 'another
data point, with
multiple lines'

pop
# Now $stackdir/data contains the popped data
cat $stackdir/data   # Print the most recently item pushed
push yet more data
pop
cat $stackdir/data   # Print 'yet more data'
pop
cat $stackdir/data
于 2012-10-05T01:37:18.017 回答
0

不幸的是,我认为 cat 的解决方案行不通。它可能适用于 Linux,但我使用的是 FreeBSD,并且我尝试使用 cat 导入临时文件的内容,但它不断失败。

cat 的问题(至少在 FreeBSD 中)是它导致 shell 将其输出解释为文字命令,并且还会跳过某些字符,这又会导致问题。

我最终的解决方案是将上述临时文件转换为变量的持有者,然后使用 source 命令导入它们。这行得通,但我不喜欢它;主要是因为我必须对 say 进行一些难看的切割,以便在数据前面加上变量名,并将其括在引号中。

因此,在数据文件中,您将拥有:-

variable=foobar

然后在脚本中,我会做任何为变量创建输出的操作,然后将其输入脚本将使用:-

source datafile

此时我可以使用该变量。

尽管如此,即使这并不像堆栈,它也可以存储数据。如果可以避免的话,我也不喜欢在 shell 脚本中使用单独的变量;主要是因为再次,这意味着诉诸丑陋的替代黑客,并且调试起来很烦人。

于 2013-11-04T16:44:19.680 回答
0

这应该是非常跨平台的。它不使用数组,因此可以在较旧的 shell 版本上工作。

Push(){ let Stack++;eval "Item$Stack=\"$1\"";}
Pop(){ eval "echo -e \$Item$Stack;unset Item$Stack";let Stack--;}
Append(){ Push "`Pop`\n$1";}

Push$Item1将数据放入, $Item2,等变量中$Item3。像这样使用它:

Push data; Push 'more data'; Push "remember to escape \"quotes\""

Pop在打印其内容后,它本身会破坏编号最高的$Item变量。要将内容存储在另一个变量中,请执行以下操作:

Variable=`Pop`

Append在堆栈顶部的变量中添加一行。例如:

Push "too much is always better than not enough"
Append "but it's almost as bad"

$Stack存储堆栈的高度。由于这些函数中没有错误处理,因此如果存在堆栈下溢,则需要重置它。更好的是,您可以检查它以防止出现 - 不要PopAppend除非$Stack是 1 或更多。

于 2018-12-21T23:42:10.160 回答
-1

Bashisms很恶心,不是吗?如果您的程序中需要数组,那么您需要使用...汇编程序(开玩笑)!好吧,这就是我在 POSIX Shell 中实现堆栈的方式:

#!/bin/sh

# --------------------
# Stack implementation
# --------------------

s=""
stk=""
STACK_MAX_SIZE="65536"

# Delete all values from the stack:
stack_clear () {
    s=""
    stk=""
}

# To push a value into the stack:
stack_push () {
    local counter
    local cnt
    counter=$(echo -n "${s}" | wc --bytes)
    cnt=$(echo -n "${counter}" | wc --bytes)
    # ----- Internal check begin -----
    check=$(echo -n "${cnt}" | wc --bytes)
    if test "${check}" != "1"
    then
        echo "Internal error: it is real to meet such a long string..."
        exit 2s
    fi
    # ----- Internal check end -----
    stk=$(echo -n "${stk}${s}${counter}${cnt}")
    local check
    check=$(echo -n "${stk}" | wc --bytes)
    if test "${check}" -gt "${STACK_MAX_SIZE}"
    then
        echo "Error: stack overflow."
        exit 1
    fi
}

# To pull a value from the stack:
stack_pop () {
    local counter
    local cnt
    if test "${stk}" = ""
    then
        echo "Error: trying to pop from an empty stack."
        exit 1
    fi
    cnt=$(echo -n "${stk}" | tail --bytes=1)
    stk=$(echo -n "${stk}" | head --bytes=-1)
    counter=$(echo -n "${stk}" | tail --bytes=${cnt})
    stk=$(echo -n "${stk}" | head --bytes=-${cnt})
    s=$(echo -n "${stk}" | tail --bytes=${counter})
    stk=$(echo -n "${stk}" | head --bytes=-${counter})
    # ----- Internal check begin -----
    local check
    check=$(echo -n "${s}" | wc --bytes)
    if test "${check}" != "${counter}"
    then
        echo "Internal error: the stack is damaged."
        exit 2
    fi
    # ----- Internal check end -----
}

# ---------------
# The entry point
# ---------------

# Push "one", "two", "three" into the stack:
s="one"; stack_push
s="two"; stack_push
s="three"; stack_push

# Extract all the data from the stack:
while test "${stk}" != ""
do
    stack_pop
    echo "${s}"
done
于 2017-06-30T00:38:58.157 回答