我需要在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”的方式来做到这一点?
提前感谢您的帮助!
答案 0 :(得分:2)
查看此处“实例27-7。空数组和空元素”部分。具体来说,评论说,上面是'推'而''pop'是:
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/
答案 1 :(得分: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
答案 2 :(得分:0)
不幸的是,我认为用cat的解决方案不会起作用。它可能适用于Linux,但我使用的是FreeBSD,我尝试使用cat来导入tempfiles的内容,并且它经常失败。
cat(至少使用FreeBSD)的问题在于它导致shell将其输出解释为文字命令,并且还会跳过某些字符,这又会导致问题。
我最终的解决方案是将所述临时文件转换为变量的持有者,然后使用source命令导入它们。这有效,但我不喜欢它;主要是因为我必须做一些丑陋的切割,以便用变量名称作为数据前缀,并用引号括起来。
所以在数据文件中,你有: -
variable=foobar
然后在脚本中,我会做任何为变量创建输出的内容,然后将其放入脚本中将使用: -
source datafile
此时我可以使用变量。
尽管如此,这并不像堆栈那样,但它可以存储数据。我不喜欢在shell脚本中使用单独的变量,如果我也可以避免它们;主要是因为它再次意味着诉诸丑陋的替换hackery,并且可能会变得烦人的调试。
答案 3 :(得分: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
存储堆栈的高度。由于这些功能没有错误处理,因此如果有堆栈下溢,则需要将其重置。
更好的是,您可以检查一下以防止出现这种情况-除非Pop
为1或更大,否则不要Append
或$Stack
。
答案 4 :(得分:-1)
Bashisms很恶心,不是吗?如果您的程序中需要数组,则需要使用... assembler (开玩笑)!好吧,这就是我在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