쉘에서 변수를 인용하는 것에 대한 답변을 여기에서 읽었습니다. 기본적으로 이것이 내가 실행하는 것입니다.
for f in "$(tmsu files)"; do echo "${f}"; done
이는 예상된 결과입니다: 파일 목록입니다. 하지만 나는 그것들이 하나의 문자열로 나타날 것이라고 확신합니다. 약간 읽은 후에 이것이 zsh-ism이라는 것을 깨달았습니다. 이는 대부분의 쉘과 다르게 분할을 처리합니다.
bash
그래서 나는 같은 명령을 실행 하고 실행했습니다. 목록이 분할될 줄 알았는데 아쉽게도정확히같은 결과. 그래서 지금은 정말 혼란스럽습니다.
- Bash가 예상대로 실행되지 않는 이유는 무엇입니까?
- zsh 분할선을 만드는 방법은 무엇입니까?
나는 (zsh)를 시도했지만 성공 for f in "$(tmsu files)"; do echo "${=f}"; done
하지
for f in "$(=tmsu files)"; do echo "${f}"; done
못했기 때문에 분할 방법을 오해한 것 같습니다 zsh
.
더 나쁜 것은 어느 시점에서 내가 원하는 대로 정확하게 작동하도록 만들었지만 어떻게 했는지 기억이 나지 않는다는 것입니다.
답변1
for f in "$(tmsu files)"; do echo "${f}"; done
이것은 zsh-ism보다 더 나쁜 습관입니다. 큰따옴표는 확장 결과를 단일 문자열로 유지하도록 쉘에 지시합니다. 대부분의 경우 이는 귀하가 원하는 것입니다. 예를 들어 다음과 같은 경우가 있습니다.
filename="foo bar"
ls "$filename"
두 인수 ls
의 합이 아닌 단일 인수로 파일 이름을 가져오려고 합니다 . (이것들은 다른 파일 이름이 될 것입니다.) 이제 명령 대체를 사용하고 있다는 사실이 이것을 바꾸지 않습니다. 이 경우 인용이 문제를 해결하지 못한다는 것은 맞지만, 이 구성은 처음에는 어색합니다. (물론 POSIX 쉘에는 zsh에 자체 도구가 있습니다).foo
bar
다음과 같은 파일 목록을 고려하십시오.
hello.txt
some silly name with spaces.txt
를 사용하면 "$(output filenames)"
두 개가 아닌 하나의 문자열이 제공됩니다.
을 사용하면 $(output filenames)
2개가 아닌 6개의 문자열이 제공됩니다.
IFS
후자가 개행 문자로 분할되어 두 개의 파일 이름을 제공하도록 개행 문자만 설정하여 이 문제를 해결할 수 있습니다 . 그러나 이는 번거롭고 전역적인 영향을 미치며 set -f
파일 이름이 바뀌는 것을 방지하려면 여전히 와일드카드를 비활성화해야 합니다 funny * name.txt
.
(지금까지는 변수 확장을 분할하지 않더라도 따옴표가 없는 명령 대체 결과를 분할한다는 점에서 기본적으로 zsh와 동일합니다. 그러나 기본적으로 결과를 와일드카드로 표시하지 않습니다.)
이제 차이점을 볼 수 없는 이유는 echo
얻은 모든 인수를 단일 공백으로 연결하여 인쇄하기 때문입니다. 따라서 정확히 동일한 출력을 생성합니다 echo foo bar
. echo "foo bar"
대신 다음과 같은 것을 사용하면 printf "<%s>\n" ...
매개변수 경계를 더 명확하게 볼 수 있습니다.
$ printf "<%s>\n" foo bar
<foo>
<bar>
$ printf "<%s>\n" "foo bar"
<foo bar>
또한보십시오:
- 분사Greg의 위키에서
- "for"가 있는 줄을 읽어보면 어떨까요?
- 파일의 줄을 반복하는 방법은 무엇입니까?("파일 줄"이라고 되어 있지만 명령 대체에 그 내용이나 다른 내용이 있는지는 중요하지 않습니다
cat
.) 항상 그렇듯이 다음과 같습니다. - 공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?
답변2
하지만 나는 그것들이 하나의 문자열로 나타날 것이라고 확신합니다.
이것이 따옴표를 사용하면 얻을 수 있는 것입니다. zsh
이 작업이 어떻게 수행되어야 하는지는 알 수 없지만 bash
설명서에는 다음과 같이 나와 있습니다("확장" 아래의 "단어 분리" 섹션).
매개변수 확장, 명령 대체 및 산술 확장에 대한 쉘 스캔 결과큰따옴표 안에는 표시되지 않습니다.단어 분할에 사용됩니다.
(강조 추가). 작은 따옴표에서는 단어 분리가 발생하지 않지만 그 안에서도 대체 및 확장이 발생하지 않습니다.
명령 대체에 별도의 매개변수를 사용하려면 따옴표를 제거해야 합니다. 그러나 tmsu
공백이 포함된 결과가 생성되면 원치 않는 분할이 생성되어(공백과 줄 바꿈 모두 단어 구분 기호로 간주되므로) 상황이 더 복잡해집니다. 에서는 행당 하나의 결과를 제공한다고 bash
가정하면 다음을 수행할 수 있습니다.tmsu
tmsu files |
while IFS= read -r f
do
printf "<%s>\n" "$f"
done
내장 read
함수는 입력의 전체 행을 읽습니다. 이름은 얼마든지 지정할 수 있습니다. 마지막 문자를 제외한 모든 문자는 단어 구분 기호(기본적으로 공백)를 기준으로 할당되고 마지막 문자는 줄의 나머지 부분에 할당되므로 bash
확장 및 교체 시 공백 처리의 번거로움을 해결하는 좋은 아이디어입니다.
답변3
- zsh 분할선을 만드는 방법은 무엇입니까?
f
인수 확장 플래그를 사용하십시오 (자세한 내용은 을 참조하십시오 man zshexpn
. 다음과 같이 말합니다: Split the result of 확장 at newlines. 이것은 의 약어입니다 ps:\n:
.)
for f in ${(f)"$( tmsu files )"}; do
print -ru2 -- $f
done
파이프 데이터를 인쇄하기 위해 stdout을 떠나 출력을 stderr로 보냅니다(아마도 출력을 인쇄하는 것보다 더 많은 일이 있을 것입니다).
도구가 ASCII 널 문자(일명 NUL, \0)로 구분된 출력을 생성할 수 있는 경우 zsh는 해당 출력을 분할하는 대신 플래그 0
(약식 )를 사용할 수 있습니다 .ps:\0:
f
빈 레코드를 보존하려면 @
플래그와 "
따옴표(예 "${(@f)"$( command )"}"
: )를 추가하세요. 명령 대체의 특성으로 인해 후행 줄 바꿈을 검색하는 것은 불가능합니다.
read
루프 에서 사용 하려면 while
zsh 또는 bash에서 다음과 같은 작업이 작동해야 합니다.
while IFS= read -ru3 -d '' x || [[ $x ]]; do
{
printf >&2 '%s\n' "$x"
z=$x
} 3<&-
done 3< <(printf 'x\0y\0z')
printf 'z: %s\n' "$z"
필드 분할이나 백슬래시 해석이 없고, 루프가 하위 쉘에서 실행되지 않고, stdin을 계속 사용할 수 있으며, 후행 구분 기호가 없어도 마지막 요소를 찾을 수 있습니다.