这是一个简单的卷曲脚本https://unix.stackexchange.com/并将结果存储到数组中,效果很好。
#!/usr/local/bin/bash
[ -f pgtoscrap ] && { rm pgtoscrap; };
curl -o pgtoscrap https://unix.stackexchange.com/;
declare -a arr;
fileName="pgtoscrap";
exec 10<&0
exec < $fileName
let count=0
while read LINE; do
arr[$count]=$LINE
((count++))
done
exec 0<10 10<&-
但是,每次我运行这个脚本时;我收到一些错误的文件描述符错误。
./shcrap
./shcrap: line 14: 10: No such file or directory
我想我不太明白如何exec
正确地在循环中使用命令。有人可以解释一下吗?
-- 实施mapfile
Bash 4 后的更新变得更加简单 --
#!/usr/local/bin/bash
## Pass a parameter as e.g. ./linkscrapping.bash https://unix.stackexchange.com/
mapfile -t arr < <(curl -s $1); ## Doing exec stuff with process substitution
regex="<a[[:print:]]*<\/a>"; ELEMENTS=${#arr[@]}; firstline=0;
for((i=0;i<$ELEMENTS;i++)); do
if [[ ${arr[${i}]} =~ $regex ]]; then
[[ $firstline<1 ]] &&
{ echo ${BASH_REMATCH[0]} > scrapped; let firstline=$firstline+1; } ||
{ echo ${BASH_REMATCH[0]} >> scrapped; }
fi
done
pg2scrap="scrapped"; mapfile -t arr2 < <(cat $pg2scrap);
regex="href=[\"\'][0-9a-zA-Z\:\/\.]+"; ELEMENTS2=${#arr2[@]}; line2=0
for ((i=0;i<$ELEMENTS2;i++)); do
if [[ ${arr2[${i}]} =~ $regex ]]; then
[[ $line2<1 ]] &&
{ echo ${BASH_REMATCH[0]#href=\"} > links; (( line2++ )); } ||
{ echo ${BASH_REMATCH[0]#href=\"} >> links; }
fi
done; cat links;
答案1
这肯定与您如何关闭之前为标准输入打开的文件描述符有关。使用下面的应该没问题
exec 10<&-
当您这样做时0<10
,您指示 shell 查找并读取10
当前目录中指定的文件的内容,这使得不在这种背景下的意义。
您bash
还可以使用另一种形式exec 10>&-
来达到关闭描述符的相同目的。
但这就是说,您不需要使用exec
随机文件描述符并读取您的输入,您只需使用进程替换技术读取您的输入,bash
其形式< <()
为
while IFS= read -r line; do
arr["$count"]="$line"
((count++))
done< <(pgtoscrap)
答案2
exec 10<&0
将文件描述符编号 0 克隆到编号 10,有效保存原始文件,以便您可以替换下一行的 fd 0 上的文件。要撤消该操作,您需要反转数字,将数字 10 克隆到数字 0 exec 0<&10
(然后使用 关闭 fd 10 exec 10<&-
)。
另一方面,exec 0<10
没有 & 符号只是带有 filename 的重定向10
。由于您没有这样的文件,因此会出现错误。
也就是说,您不需要使用exec
while 循环临时设置重定向。复合命令也可以进行重定向,如下所示:
while read LINE; do
...
done < "$filename"
如果您想按原样读取整行,而没有空格或反斜杠影响数据,则需要取消设置IFS
,read
并使用read -r
。另外,如果要附加到数组,则无需手动跟上索引,只需使用+=
直接附加到数组即可:
arr=() # declares it an array and clears it, not strictly necessary though
while IFS= read -r line; do
arr+=("$line")
done < "$filename"
或者使用mapfile
( readarray
) 而不是像@BlackJack 在评论中提到的手动循环:
mapfile -t arr < "$filename"
或者甚至根本没有临时文件:
#/bin/bash
mapfile -t arr < <(curl -s https://unix.stackexchange.com/)
(如果没有-t
,mapfile
则将行终止符保留在原位。)