For 루프 및 텍스트 파일 읽기

For 루프 및 텍스트 파일 읽기

for 루프가 예상한 대로 작동하지 않는 이유는 무엇입니까?

내 스크립트 파일은 다음과 같습니다(Ventura OS 13.2.1이 설치된 MAC 컴퓨터에서 zsh를 실행하고 있습니다).

#!/bin/zsh
 
for user in "$(cat $1)";
do
        website="myWebSite.org/@/"
        url=$website$user
        echo "here is the url: $url"
done

여기 내 데이터 파일이 있습니다. 한 줄에 하나씩 4개의 사용자 이름이 있습니다.

cat userList.txt 
user_1
user_2
user_3
user_4

나는 출력이 다음과 같을 것으로 기대합니다.

myWebSite.org/@/user_1
myWebSite.org/@/user_2
myWebSite.org/@/user_3
myWebSite.org/@/user_4

대신에 내가 얻는 결과는 다음과 같습니다.

./findusers userList.txt 
here is the url: myWebSite.org/@/user_1
user_2
user_3
user_4

나는 몇 시간 동안 인터넷 검색을 해왔지만 이런 유형의 문제에 가까운 것을 찾을 수 없습니다. 쉘이 내가 볼 수 없는 일부 파일을 조작하는 것과 거의 비슷하며 for 루프의 echo 명령이 한 번만 실행되는 이유를 이해할 수 없습니다. 내 시스템에 있는 오래된 파일의 버전 문제인가요? 도움을 주셔서 감사합니다. zsh 코딩은 제 본업이 아닙니다. 감사합니다!

답변1

명령 대체를 분할해야 합니다.

for user in $(<$1)

따옴표가 없으면 기본값은 $(...)공백, 탭, 줄 바꿈 및 널 문자로 분할되는 것입니다. Korn과 유사한 연산자는 여기에서 대신 최적화로 $IFS사용됩니다 .$(<file)$(cat -- $1)

줄 바꿈(일명 line feed)으로만 분할하려면 동일한 작업을 수행하되 인수 확장 플래그(약어로 약칭 )를 IFS=$'\n'사용하거나 사용하십시오 .fps[\n]

for user in ${(f)"$(<$1)"}

IFS 분할을 방지하기 위해 따옴표를 기록한 다음 f개행 문자에서 분할할 플래그를 기록하십시오.

while read루프를 사용할 수도 있습니다 .

while IFS= read -ru3 user; do
  ...
done 3< $1

이전 방법과의 한 가지 차이점은 빈 줄을 건너뛰지 않는다는 것입니다.

또한 마지막 줄 바꿈 뒤의 문자가 있으면 건너뛰지만 이러한 문자는 텍스트 파일에서 허용되지 않습니다.

전체 파일을 메모리에 저장하는 것을 방지하지만, 반면에 각 파일은 read줄을 구분하는 개행 문자 이상을 읽지 않도록 해야 하므로 파일을 한 번에 1바이트씩 읽습니다.

그리고:

for user in "${(f@)$(<$1)}"

또는:

IFS=$'\n\n'
for user in $(<$1)

명령 대체 표시줄로 빈 줄(후행 공백 줄 제외)을 남겨 둡니다.모두후행 개행 문자.

모든 줄을 배열로 읽으면서 빈 줄과 마지막 개행(있는 경우) 뒤의 바이트로 구성된 비줄을 고려하고 이를 반복하는 것은 매우 어색해 지므로 도우미 함수를 사용할 수 있습니다.

lines() {
  local ret
  reply=( "${(@f)$(cat -- "$@"; ret=$?; echo .; exit $ret)}" )
  ret=$?
  reply[-1]=( ${reply[-1]%.} )
  return $ret
}
lines myfile &&
  for line in "$reply[@]"; do
    something with "$line"
  done

또한 echo임의의 데이터 출력은 피해야 하며( 의 경우에는 zsh실제로 사용할 수 있지만) 다른 쉘이나 Korn 쉘과 마찬가지로 사용하는 것이 가장 좋습니다 echo -E - $data.printf '%s\n' "$data"print -r -- "$data"


1 다른 POSIX 유사 셸(예: bash)과 달리 zsh에는 기본적으로 결과가 와일드카드의 영향을 받는 결함이 없으므로 거기 에 set -o noglob// 같은 것을 가질 필요가 없습니다 .shbashksh

답변2

당신이 말하길(댓글에서), 입력 파일의 각 줄을 가져와 URL 앞에 URL을 추가한 다음 에 대한 호출에 사용하려고 합니다 curl.

curl아마도 가장 좋은 방법은 양식에 몇 줄로 구성 파일을 작성하는 것입니다.url

url = http://some/url

그런 다음 해당 파일을 단일 호출로 전달합니다 curl.

이것을하기 위해:

curl --config <( sed 's|^|url = http://example.com/|' file )

line.out방문한 각 URL의 출력을 이름이 지정된 파일 ( line파일에서 읽은 행이 있는 위치) 에 저장하려면 output각 URL에 대해 하나의 명령문을 삽입하기만 하면 됩니다.

GNU를 사용하는 방법은 다음과 같습니다 sed.

curl --config <( sed 's|.*|url = http://example.com/&\noutput = &.out|' file )

또는 다음을 사용하십시오 awk.

curl --config <( awk '{ printf "url = http://example.com/%s\noutput = %s.out\n", $0, $0 }' file )

마지막 두 명령은 입력 파일의 줄에 간단한 단어가 포함되어 있다는 것을 알고 있다고 가정합니다. 문자열에 절대 또는 상대 경로 이름이나 curl유틸리티별 패턴이 포함되어 있는 경우 먼저 삭제해야 할 수도 있습니다.

답변3

또한 for 루프에서 파일을 읽는 문제가 발생했습니다.

이를 위해 while 루프를 사용합니다.

FILE="some_items.txt"
LINES=$(cat $FILE|wc -l)
INDEX=0

while [ $INDEX -lt $LINES ]
do
   LN=$((INDEX + 1))
   ITEM="$(cat $FILE|head -n $LN|tail -n 1)"
   echo "current item is $ITEM"
   INDEX=$((INDEX + 1))
done

이것은 zsh, bash 및 sh에서 작동합니다.

관련 정보