쉘이 "cat의 쓸모없는 사용"을 자동으로 수정하지 않는 이유는 무엇입니까? [폐쇄]

쉘이 "cat의 쓸모없는 사용"을 자동으로 수정하지 않는 이유는 무엇입니까? [폐쇄]

많은 사람들이 다음을 포함하는 한 줄짜리 스크립트와 스크립트를 사용합니다.

cat "$MYFILE" | command1 | command2 > "$OUTPUT"

첫 번째 cat는 기술적으로 새로운 프로세스(보통 /usr/bin/cat)를 시작해야 하기 때문에 "cat의 쓸모없는 사용"이라고 불리는 경우가 많습니다. 이는 명령이 이미 실행된 경우 피할 수 있습니다.

< "$MYFILE" command1 | command2 > "$OUTPUT"

그 이유는 쉘을 시작 하고 주어진 파일을 command1가리키기만 하면 되기 때문입니다.stdin

쉘이 이 변환을 자동으로 수행하지 않는 이유는 무엇입니까?"쓸데없는 고양이 사용" 구문이 읽기 더 쉽고, 쓸모없는 고양이를 자동으로 제거할 수 있는 충분한 정보가 쉘에 있어야 한다고 생각합니다. 이는 catPOSIX 표준에 정의되어 있으므로 경로에 바이너리를 사용하는 대신 쉘이 내부적으로 이를 구현할 수 있도록 허용해야 합니다. 셸은 구현의 인수 버전을 포함하고 경로의 바이너리로 대체할 수도 있습니다.

답변1

"쓸데없는 사용 cat"은 스크립트가 실행될 때 실제로 실행되는 것보다 코드가 작성되는 방식에 관한 것입니다. 이건 디자인이야안티패턴, 보다 효율적인 방법으로 작업을 수행할 수 있는 방법입니다. 새로운 도구를 만들기 위해 주어진 도구를 결합하는 최선의 방법을 이해하지 못했습니다. 파이프라인에서 여러 명령 sed및/또는 명령을 함께 묶는 것은 awk때때로 동일한 안티 패턴의 증상이라고 말할 수 있다고 생각합니다.

스크립트의 "낭비 사용" 상황을 수정하려면 cat주로 스크립트의 소스 코드를 수동으로 수정해야 합니다. 다음과 같은 도구주택 검사다음과 같은 명백한 사례를 지적하면 이 문제를 해결하는 데 도움이 될 수 있습니다.

$ cat script.sh
#!/bin/sh
cat file | cat
$ shellcheck script.sh

In script.sh line 2:
cat file | cat
    ^-- SC2002: Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.

쉘 스크립트의 특성으로 인해 쉘이 이 작업을 자동으로 수행하도록 하는 것은 어려울 수 있습니다. 스크립트가 실행되는 방법은 상위 프로세스에서 상속된 환경과 사용 가능한 외부 명령의 특정 구현에 따라 달라집니다.

쉘은 cat그것이 무엇인지 반드시 알 필요는 없습니다. 그럴 수도 있다어느$PATH또는 함수의 어느 곳에서나 명령을 내릴 수 있습니다.

내장 명령인 경우(일부 셸에 있을 수 있음)회의cat내장된 명령의 의미를 알고 있기 때문에 파이프라인을 재구성할 수 있습니다 . 그 전에는 파이프라인에서 원래 명령 이후의 다음 명령에 대해서도 가정해야 합니다 cat.

표준 입력에서 읽는 것은 파일에 연결할 때와 파이프에 연결할 때 약간 다르게 동작합니다. 파이프는 검색 가능하지 않으므로 파이프라인의 다음 명령이 수행하는 작업에 따라 파이프를 다시 정렬하면 다르게 동작할 수도 있고 그렇지 않을 수도 있습니다. 그렇지 않은 경우 어쨌든 다르게 동작합니다).

이 질문은 비슷합니다 (매우일반적인 의미에서) "컴파일러가 자체적으로 구문 오류를 수정하려고 합니까?"(소프트웨어 엔지니어링 StackExchange 사이트에서) 이 질문은 분명히 구문 오류에 관한 것이지 쓸모없는 디자인 패턴에 관한 것은 아닙니다. 하지만 의도에 따라 코드를 자동으로 변경한다는 아이디어는 기본적으로 동일합니다.

답변2

왜냐하면 그것은 쓸모가 없기 때문입니다.

의 경우 cat file | cmdfd 0(stdin)는 cmd파이프가 되고, 의 경우 cmd <file일반 파일, 장치 등이 될 수 있습니다.

파이프는 일반 파일과 다른 의미를 가지며, 그 의미는 다음과 같습니다.아니요일반 파일의 하위 집합:

  • 일반 파일은 의미 있는 방식으로 select(2)편집하거나 편집 할 수 없습니다. poll(2)a는 select(2)항상 "준비"를 반환합니다. Linux와 같은 고급 인터페이스는 epoll(2)일반 파일을 처리할 수 없습니다.

  • Linux에는 파이프에서만 작동하는 일부 시스템 호출( splice(2), vmsplice(2), ) 이 있습니다 [1]tee(2)

매우 일반적으로 사용 되므로 cat이를 내장 셸로 구현하여 추가 프로세스를 피할 수 있지만 일단 해당 경로로 이동하기 시작하면 대부분의 명령은 동일한 작업을 수행합니다. perl즉 셸을 더 느리고 더 투박하게 만듭니다. python. 파이프와 같은 구문을 사용하기 쉬운 다른 스크립팅 언어를 작성하는 것이 더 좋습니다.계속하다대신에;-)

[1] 그냥 만들어지지 않은 간단한 예를 원한다면 내 "exec bin from stdin" git을 확인해 보세요.요점그리고 댓글에 몇 가지 설명이 있습니다여기. UUoC 없이 작동하도록 내부적으로 구현하면 cat크기가 2~3배 증가합니다.

답변3

이 두 명령은 동일하지 않습니다. 오류 처리를 고려하십시오.

cat <file that doesn't exist> | less파이프 프로그램에 전달될 빈 스트림을 생성하므로 결국 아무것도 표시하지 않는 디스플레이가 됩니다.

< <file that doesn't exist> less바를 열 수 없을 것이고, 전혀 열리지 않을 것입니다.

전자를 후자로 변경하려고 하면 입력이 비어 있을 가능성이 있는 프로그램을 실행하려는 스크립트가 여러 개 중단될 수 있습니다.

답변4

간단히 말해서:비용이 가능한 이점보다 크기 때문에 쉘은 이 작업을 자동으로 수행하지 않습니다.

다른 답변은 stdin을 파이프로 사용하는 것과 파일로 사용하는 것의 기술적 차이점을 지적합니다. 이를 염두에 두고 셸은 다음 중 하나를 수행할 수 있습니다.

  1. cat내장된 구현으로서 파일과 파이프 사이의 구별은 여전히 ​​유지됩니다. 이를 통해 실행 비용은 물론 포크 비용까지 절감할 수 있습니다.
  2. 파일/파이프라인이 중요한지 확인하는 데 사용되는 다양한 명령을 배우고 파이프라인에 대한 철저한 분석을 수행한 다음 그에 따라 행동합니다.

다음으로, 각 접근 방식의 비용과 이점을 고려해야 합니다. 이점은 간단합니다.

  1. 두 경우 모두 exec (of cat) 를 피하십시오.
  2. 두 번째 경우에는 리디렉션 교체가 가능하면 분기를 피할 수 있습니다.
  3. 파이프를 사용해야 하는 경우 다음을 수행할 수 있습니다.가능한때때로 fork/vfork를 피할 수 있지만 일반적으로 그렇지 않습니다. 이는 cat 해당 항목이 나머지 파이프라인과 동시에 실행되어야 하기 때문입니다.

따라서 특히 분기를 피할 수 있는 경우 약간의 CPU 시간과 메모리를 절약할 수 있습니다. 물론 실제로 이 기능을 사용할 때만 시간과 메모리가 절약됩니다. 그리고 실제로는 포크/실행 시간만 절약됩니다. 더 큰 파일의 경우 시간은 대부분 I/O 시간입니다(예: 고양이가 디스크에서 파일을 읽는 경우). 따라서 질문해야 합니다. cat성능이 정말로 중요한 쉘 스크립트에서 얼마나 자주 (쓸모 없이) 사용됩니까? 다른 일반적인 쉘 내장 기능과 비교해 보세요 . 중요한 장소에서 10분의 1의 시간 test이라도 (쓸모 없이) 사용된다는 것을 상상하기 어렵습니다 . 이는 추측일 뿐, 아직 측정해 본 적은 없으며, 구현하기 전에 꼭 해보고 싶은 일입니다. (또는 유사하게 기능 요청 등을 통해 다른 사람에게 구현하도록 요청하세요.)cattest

다음으로 질문합니다. 비용은 얼마입니까? 염두에 두어야 할 두 가지 비용은 다음과 같습니다. (a) 크기를 늘리고 잠재적으로 메모리를 사용하는 셸의 추가 코드는 더 많은 유지 관리가 필요하며 버그가 나타날 또 다른 장소입니다. (b) ) 이전 버전과의 호환성 놀랍게도 POSIX에는 catGNU coreutils와 같은 많은 기능이 생략되어 있으므로 내장 기능이 무엇을 구현할 것인지 cat주의 깊게 살펴보아야 합니다 .cat

  1. 추가 내장 옵션도 그리 나쁘지 않을 수 있습니다. 이미 내장 옵션이 많이 있는 곳에 하나를 더 추가하세요. 프로파일링 데이터가 도움이 될 것으로 나타나면 즐겨찾는 셸 작성자에게 이를 추가하도록 설득할 수 있습니다.

  2. 파이프 구문 분석에 관해서는 쉘이 현재 그런 작업을 수행하지 않는다고 생각합니다(일부 쉘은 파이프 끝을 인식하고 분기를 피할 수 있습니다). 본질적으로 셸에 (원시) 최적화 프로그램을 추가하는 것입니다. 최적화 프로그램은 종종 복잡한 코드가 되고 많은 버그의 원인이 됩니다. 이러한 오류는 놀랄 수 있습니다. 쉘 스크립트의 작은 변경으로 인해 오류가 발생하지 않거나 발생할 수 있습니다.

추신:cat의 쓸모없는 사용에도 비슷한 분석을 적용할 수 있습니다. 장점: 읽기가 더 쉽습니다(command1이 파일을 인수로 사용하는 경우에는 그렇지 않을 수도 있음). 비용: 추가 포크 및 실행(command1이 파일을 인수로 사용할 수 있는 경우 잠재적으로 더 혼란스러운 오류 메시지). 분석을 통해 cat을 사용하는 것이 쓸모없다고 판단되면 계속 진행하세요.

관련 정보