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


42

다음은 출력 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에 공백이있는 List 요소 에 대한 답변에 IFS따라 입력을 나누고 요소를 배열로 수집하는 방법을 zsh에 알려주는 대신 매개 변수 확장 플래그를 사용해 보았습니다 .

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

하지만 난 여전히 (분할가 일어나고 같은 결과를 얻었다 n)

이것으로 다음과 같은 질문이 있습니다.

Q1. 여러 줄로 된 명령의 출력을 수집하는 "적절한" 방법은 무엇입니까 ?

Q2. IFS바꿈 만 나누도록 지정 하려면 어떻게해야 합니까?

Q3. 위의 세 번째 시도에서와 같이 매개 변수 확장 플래그를 사용하여 (예 @f:) 분할을 지정하면 zsh는 IFS? 왜 위에서 작동하지 않았습니까?

답변:


71

TL, DR :

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인쇄하기 위해 분할 된 명령을 사용했다고 의심됩니다 $array_of_lines(→ Q3).


7
너무 복잡합니다! "${(@f)...}"와 동일 ${(f)"..."}하지만 다른 방식입니다. (@)큰 따옴표 안에는 "배열 요소 당 하나의 단어를 생성"을 (f)의미하고 "개행으로 배열에 분리 "를 의미합니다. 추신 : 문서에
날으는 양

3
@flyingsheep, ${(f)"..."}빈 줄을 건너 뛰지 않고 "${(@f)...}"보존합니다. 그 사이 같은 구분의 $argv"$argv[@]". 즉, "$@"배열의 모든 요소를 보존하는 것은 70 년대 후반의 Bourne 쉘에서 온다.
Stéphane Chazelas

4

두 가지 문제 : 첫째, 분명히 큰 따옴표는 백 슬래시 이스케이프를 해석하지 않습니다 (죄송합니다 :). $'...'따옴표를 사용하십시오 . 에 따르면 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

Q3에 답할 수 없습니다. 나는 그런 난해한 것들을 알 필요가 없기를 바랍니다 :).


-2

tr을 사용하여 줄 바꿈을 공백으로 바꿀 수도 있습니다.

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

3
줄에 공백이 있으면 어떻게됩니까?
don_crissti

2
말이되지 않습니다. SPC와 NL은 모두 기본값 인입니다 $IFS. 하나를 다른 것으로 번역해도 차이가 없습니다.
Stéphane Chazelas

편집 내용이 합리적 이었습니까? 나는 그것을 그대로 작동시킬 수 없었습니다
John P

(시간 초과) 의도가 무엇인지 실제로 이해하지 않고 편집했음을 인정하지만 번역은 문자열 조작을 기반으로 분리하기에 좋은 시작이라고 생각합니다. echo입력이 모두 관심있는 사람에 의해 분리 된 단어의 힙이 거의 같지 않은 경우 예상되는 동작이 더 비슷하지 않으면 공백으로 변환하지 않고 분할합니다 .
John P
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.