bash 循环中的 exec 命令用于网页抓取

bash 循环中的 exec 命令用于网页抓取

这是一个简单的卷曲脚本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正确地在循环中使用命令。有人可以解释一下吗?

-- 实施mapfileBash 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。由于您没有这样的文件,因此会出现错误。


也就是说,您不需要使用execwhile 循环临时设置重定向。复合命令也可以进行重定向,如下所示:

while read LINE; do
    ...
done < "$filename"

如果您想按原样读取整行,而没有空格或反斜杠影响数据,则需要取消设置IFSread并使用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/)

(如果没有-tmapfile则将行终止符保留在原位。)

相关内容