2012-10-04 61 views
4

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

我尝试使用堆栈文件:

#!/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 

这完美的作品,但感觉不对。有没有一个“黑客”的方式来做到这一点?

在此先感谢您的帮助!

+3

要将文件的打印线2,它是非常清洁的使用'sed的-n 2p'而不是管道尾部头。 –

回答

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/

+0

我不能使用数组,因为它是bash的特定功能=(我需要它尽可能便携 –

+0

@JanitoVaqueiroFerreiraFilho不要在你的帖子上标记“bash”如果是这样的话''zsh','ksh ''和'tcsh'都有阵列,但它不是POSIX的一部分。 – jordanm

+0

好的。对不起,错误=(并感谢修复标签。 –

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 
0

不幸的是,我不认为有猫的解决方案是可行的。它可能在Linux中工作,但我使用FreeBSD,并试图使用cat来导入临时文件的内容,并且它不断失败。

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

我最终的解决方案是将所述的tempfiles转换为持有者的变量,然后用source命令导入它们。这有效,但我不喜欢它;主要是因为我必须做一些难看的切割,以便用变量名称加前缀数据,并将其包含在引号中。

所以在数据文件中,你必须: -

variable=foobar 

然后在剧本,我会做什么创造了我的可变输出,然后进入它的脚本将使用: -

source datafile 

在这一点上,我可以使用该变量。

尽管这看起来并不像一个堆栈,但它可以存储数据。我不喜欢在shell脚本中使用单独的变量,如果我可以避免它们;主要是因为再次,这意味着诉诸丑陋的替代hackery,并可能变得烦人的调试。

-1

Bashisms令人厌恶,不是吗?如果你的程序需要数组,那么你需要使用...汇编(开玩笑)!那么,这是怎么实现的堆栈在POSIX外壳:

#!/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