다음은 간단한 컬 스크립트입니다.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
루프 내에서 명령을 올바르게 사용하는 방법을 잘 이해하지 못하는 것 같습니다 . 누군가 이것을 설명할 수 있나요?
-- Bash 4를 구현한 mapfile
후 업데이트가 더 쉬워졌습니다 --
#!/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
이는 이전에 stdin용으로 열었던 파일 설명자를 닫는 방법과 관련이 있을 것입니다. 다음을 사용하면 괜찮을 것입니다
exec 10<&-
이렇게 하면 현재 디렉터리에 지정된 파일의 내용을 0<10
찾아 읽도록 쉘에 지시하게 됩니다 .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
앰퍼샌드 없이 파일 이름을 사용한 리디렉션만 있습니다 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"
또는 주석에 언급된 @BlackJack과 같은 수동 루프 대신 mapfile
( )를 사용하십시오.readarray
mapfile -t arr < "$filename"
또는 임시 파일이 전혀 없는 경우도 있습니다.
#/bin/bash
mapfile -t arr < <(curl -s https://unix.stackexchange.com/)
(그렇지 않은 경우 -t
줄 mapfile
종결자는 그대로 유지됩니다.)