bash 3에서 파이프와 문자열의 차이점

bash 3에서 파이프와 문자열의 차이점

내가 실행하면 :

IFS=':' cat <<< $(echo $PATH)

그런 다음 ":"는 단어 분할에 사용되며 출력에는 ":" 대신 공백이 포함됩니다. 그러나 다음을 실행하면:

echo $PATH | IFS=':' cat

그렇지 않습니다.

파이프에 대한 나의 이해는 fd 1 of 를 fd 0 of 에 a | b연결한다는 것입니다 . 그리고 herestrings에 대한 나의 이해 는 그들이 문자열을 .fd 0 에 직접 쓴다는 것입니다 . 두 경우 모두 동일한 내용이 cat의 fd 0에 기록되어야 하는 것처럼 보입니다.aba <<< stringa

이는 두 명령 사이에 차이가 없어야 함을 의미하지만, 차이점을 보여주기 때문에 제가 오해하고 있는 부분이 분명히 있을 것입니다.

diff <(echo $PATH | IFS=':' cat) <(IFS=':' cat <<< $(echo $PATH))

나는 어떤 일을 완수하려고 노력하지 않고, 그것을 끝내기 위해 잘라서 붙여넣을 수 있는 텍스트를 요청하는 것이 아닙니다. 나는 나의 이해와 모순되는 상황을 설명하고 나의 오해에 대한 설명을 요청했습니다.

이 두 명령이 서로 다른 출력을 생성하는 이유를 누군가 설명할 수 있습니까?


방금 bash 4에서 시도했지만 아무런 차이가 없었습니다. 그래서 "Bash의 버전은 무엇입니까?"라고 물었습니다... 그래서 질문을 bash 3으로 제한하도록 편집했습니다. 나는 bash 4에 대해 묻는 것이 아닙니다.

다시 말하지만, 질문은 "내가 아무 생각 없이 잘라내고 붙여 넣을 수 있는 것을 누군가 나에게 줄 수 있는가"가 아닙니다. 문제는 "이 두 가지의 차이점은 무엇입니까?"입니다.

답변1

일부 소프트웨어의 이전 버전에서 이상한 동작이 나타나는 경우 수정된 버그/버그 기능 때문일 가능성이 높습니다. 일반적으로 최신 소프트웨어 버전을 사용하는 것이 좋습니다.

IFS새로운 가치

IFS=':' cat <<< $(echo $PATH)

에서처럼 단어 분할 동작에 영향을 주어서는 안 됩니다 IFS=':' echo $PATH. 또는 마찬가지로 의 새 값을 에서 사용해서는 안 됩니다 var.var=xyz cat <<< "$var"var=xyz echo "$var"

그러나 이것은 Bash 4.0 이전 버전의 here-string에서 발생하며 제가 테스트한 다른 쉘에서는 발생하지 않습니다. 다른 확장 기능의 작동 방식 및 내가 시도한 다른 쉘과 일치하지 않는다는 사실은 이러한 동작이 없다는 사실이 버그임을 시사합니다. 또한 Bash 4.1로 변경되었으므로 Bash 관리자도 이를 수행해야 한다고 생각하지 않는 것 같습니다.

CHANGES 파일에는 bash-4.1-alphahere-strings가 아닌 here-docs만 명시적으로 언급하는 에 대한 변경 사항이 포함되어 있지만 그렇지 않으면 관련성이 있는 것처럼 들립니다.

jj. Fixed a bug that caused variable expansion in here documents to look in
    any temporary environment.

버그가 있는 동작은 여기서 문자열이 있는 명령과 파이프가 있는 명령 간의 차이점을 설명합니다.


다소 관련된 질문으로 여기 문자열이 먼저 토큰화된다는 사실도 버그입니다. 일반 cmd <<< $var할당 을 통해 여러 문자열을 할당할 수 없는 것처럼 here-string을 통해 여러 다른 문자열을 전달할 수 있는 방법이 없으므로 foo=$var.

z.  Bash no longer splits the expansion of here-strings, as the documentation
    has always said.

어쨌든, 이 두 명령은 다음과 같습니다.

echo $foo | somecmd 
somecmd <<< $(echo $foo)

두 가지 버그를 수정하더라도 완전히 동일하지는 않습니다. 명령 대체는 후행 줄 바꿈을 모두 제거하고 여기 문자열에 정확히 1이 추가되는 반면 파이프는 전달된 데이터를 수정하지 않습니다.

기본 메커니즘도 매우 다릅니다. 에서는 cmd2 <<< $(cmd1)셸이 cmd1시작하기 전에 전체 출력을 읽어야 할 수 있지만 cmd2에서는 cmd1 | cmd2두 출력이 병렬로 실행되고 셸이 데이터를 건드릴 필요가 없습니다.

또한 언제나 그렇듯이, 수정되지 않은 변수 값을 어디에서나 사용하려면 확장을 큰따옴표로 묶어야 합니다.

답변2

효과적 일 수 없습니다 IFS=':' cat. IFS환경 변수로 가져오고 있습니다 cat. 할당은 cat프로세스에만 표시됩니다. cat아무것도 하지 마세요 IFS.

첫 번째 예제의 이 버전은 분할 동작을 재현합니다.

(IFS=':'; cat <<< $(echo $PATH))

IFS범위가 할당 되도록 서브셸에서 실행하기 위해 그 주위에 괄호를 넣었습니다 . IFS명령 대체 결과를 $(echo ...)확장 하면 값이 유효하기 때문에 작동합니다 .

이 행동은*확실히문서와 일치하지 않습니다. Bash 매뉴얼 페이지에는 <<<word단어 분리가 수행되지 않는다고 나와 있습니다. 그러나 그것이 바로 분할의 목적입니다 IFS. 그것은 단어 분할입니다.

실제로 일어난 일은이를 $PATH명령에 대체하는 것은 다음을 기반으로 단어 분할을 echo수행하는 것입니다 .IFS.

cat <<< $(echo $PATH)명령을 실행하기 전에 먼저 확장하십시오 $PATH. 이것이 IFS작동하는 곳입니다. 이로 echo인해 공간적으로 경로가 분할됩니다. 전체 출력이 word매개변수가 되며 <<< word더 이상 분할되지 않습니다.

각 실험의 동작을 고려하십시오.

# <<< prevents word splitting

$ (IFS=':'; abc='a:b:c'; cat <<<$abc )  # <<< prevents word splitting
a:b:c

# quotes prevent word splitting

$ (IFS=':'; abc='a:b:c'; cat <<<$(echo "$abc"))
a:b:c

# lack of quotes allows word splitting (same as $PATH example)

$ (IFS=':'; abc='a:b:c'; cat <<<$(echo $abc))
a b c

# Proof that a:b:c is already split coming from echo

$ (IFS=':'; abc='a:b:c'; cat <<<$(echo $abc | tr ' ' -))
a-b-c

관련 정보