zsh에서 행 배열을 올바르게 수집하는 방법

zsh에서 행 배열을 올바르게 수집하는 방법

다음은 출력을 my_command행 배열로 그룹화한다고 생각합니다.

IFS='\n' array_of_lines=$(my_command);

따라서 이는 $array_of_lines[1]출력의 첫 번째 줄 my_command, $array_of_lines[2]두 번째 줄 등을 참조합니다.

그러나 위의 명령은 잘 작동하지 않는 것 같습니다. 내가 확인한 대로 my_command출력을 문자별로 분할하는 것 같습니다. 이는 배열의 요소를 한 줄씩 인쇄하는 것으로 생각됩니다. 나는 또한 이것을 확인했습니다 :nprint -l $array_of_lines

echo $array_of_lines[1]
echo $array_of_lines[2]
...

두 번째 시도에서 다음을 eval추가하면 도움이 될 것이라고 생각했습니다.

IFS='\n' array_of_lines=$(eval my_command);

그러나 나는 그것이 없는 것과 똑같은 결과를 얻습니다.

마지막으로 답변을 따르세요.zsh에서 공백이 있는 요소 나열IFS, 또한 zsh에게 입력을 분할하고 요소를 배열로 수집하는 방법을 알려주는 대신 매개변수 확장 플래그를 사용해 보았습니다 . 즉:

array_of_lines=("${(@f)$(my_command)}");

하지만 여전히 동일한 결과를 얻습니다(분할은 에서 발생함 n).

이와 관련하여 다음과 같은 질문이 있습니다.

Q1.무엇인가요"옳은"여러 줄의 명령 출력을 수집하는 방법은 무엇입니까?

Q2.IFS개행만으로 분할을 지정하는 방법은 무엇입니까 ?

Q3.위의 세 번째 시도에서처럼 매개변수 확장 플래그(즉, 사용)를 사용하여 분할을 지정하면 @fzsh가 값을 무시합니까 IFS? 위의 방법이 왜 작동하지 않습니까?

답변1

TL, 박사:

array_of_lines=("${(@f)$(my_command)}")

첫 번째 오류(→ Q2): 두 문자로 IFS='\n'설정 하고 . 줄 바꿈을 설정하려면 를 사용하십시오 .IFS\nIFSIFS=$'\n'

두 번째 오류: 변수를 배열 값으로 설정하려면 요소 주위에 괄호가 필요합니다 array_of_lines=(foo bar).

연속된 공백이 단일 구분 기호로 처리되기 때문에 빈 줄을 제거한다는 점을 제외하면 작동합니다.

IFS=$'\n' array_of_lines=($(my_command))

다음에서 공백 문자를 두 배로 늘려 마지막 줄을 제외하고 빈 줄을 유지할 수 있습니다 IFS.

IFS=$'\n\n' array_of_lines=($(my_command))

후행 빈 줄을 유지하려면 명령 출력에 무언가를 추가해야 합니다. 이는 구문 분석이 아닌 명령 대체 자체에서 발생하기 때문입니다.

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'

(출력이 my_command구분되지 않은 줄로 끝나지 않는다고 가정하고 종료 상태도 잃게 됩니다 my_command.)

위의 모든 코드 조각은 IFS기본값이 아닌 값을 유지하므로 후속 코드를 엉망으로 만들 수 있습니다. 로컬 설정을 유지하려면 IFS전체를 IFS로컬을 선언하는 함수에 넣으세요(여기서 명령의 종료 상태도 유지된다는 점에 유의하세요).

collect_lines() {
  local IFS=$'\n\n' ret
  array_of_lines=($("$@"; ret=$?; echo .; exit $ret))
  ret=$?
  unset 'array_of_lines[-1]'
  return $ret
}
collect_lines my_command

그러나 나는 그것을 함부로 다루지 않는 것을 권장합니다 IFS. 대신 f에 확장 플래그를 사용하여 개행 문자로 분할합니다(→ Q1).

array_of_lines=("${(@f)$(my_command)}")

또는 뒤에 빈 줄을 유지하십시오.

array_of_lines=("${(@f)$(my_command; echo .)}")
unset 'array_of_lines[-1]'

거기에서는 의 가치가 IFS중요하지 않습니다. 테스트에서 분할 IFS인쇄 명령을 사용한 것 같습니다(→ Q3).$array_of_lines

답변2

두 가지 문제: 첫째, 분명히 큰따옴표는 백슬래시 이스케이프를 설명하지 않습니다(죄송합니다 :). $'...'따옴표를 사용하세요 . 에 따르면 man zshparam단어를 배열로 수집하려면 해당 단어를 괄호로 묶어야 합니다. 그래서 이것은 작동합니다 :

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
c
d
e f
% print $arr[1]
a b

귀하의 질문에 답변을 드릴 수 없습니다 3. 그렇게 심오한 것을 알 필요가 없기를 바랍니다 :).

답변3

tr을 사용하여 개행 문자를 공백으로 바꿀 수도 있습니다.

lines=($(mycommand | tr '\n' ' '))
select line in ("${lines[@]}"); do
  echo "$line"
  break
done

관련 정보