예, 다음과 같은 여러 가지가 있습니다.
while read line; do
echo $line | cut -c3
done
또는 더 나쁜 :
for line in `cat file`; do
foo=`echo $line | awk '{print $2}'`
echo whatever $foo
done
(웃지 마, 나는 그중 많은 것을 보았습니다).
일반적으로 쉘 스크립팅 초보자가 제공합니다. 이것들은 C 나 파이썬과 같은 명령형 언어로 할 것의 순전 한 문자 그대로의 번역이지만 쉘에서하는 일이 아니며, 그 예는 매우 비효율적이며 완전히 신뢰할 수 없으며 (잠재적으로 보안 문제를 일으킬 수 있음) 대부분의 버그를 수정하기 위해 코드를 읽을 수 없게됩니다.
개념적으로
C 또는 대부분의 다른 언어에서 빌딩 블록은 컴퓨터 명령보다 한 단계 위입니다. 프로세서에 수행 할 작업과 다음에 수행 할 작업을 알려줍니다. 당신은 당신의 프로세서를 손으로 잡고 그것을 마이크로 관리합니다 : 당신은 그 파일을 열고, 많은 바이트를 읽습니다.
쉘은 고급 언어입니다. 언어조차도 말할 수 없습니다. 그들은 모든 명령 줄 인터프리터 앞에 있습니다. 작업은 사용자가 실행하는 명령으로 수행되며 셸은 명령을 조정하기위한 것입니다.
유닉스가 도입 한 가장 큰 장점 중 하나는 파이프 와 모든 명령이 기본적으로 처리하는 기본 stdin / stdout / stderr 스트림이었습니다.
45 년 동안, 우리는 명령의 힘을 활용하고 작업에 협력하도록 API보다 나은 것을 찾지 못했습니다. 아마도 오늘날 사람들이 여전히 껍질을 사용하는 주된 이유 일 것입니다.
절단 도구와 음역 도구가 있으며 간단하게 수행 할 수 있습니다.
cut -c4-5 < in | tr a b > out
쉘은 배관 작업을 수행하고 있으며 (파일을 열고 파이프를 설정하고 명령을 호출 함) 모든 준비가 완료되면 쉘이 아무 작업도하지 않고 흐릅니다. 이 도구는 충분한 버퍼링으로 자신의 속도에 맞춰 작업을 동시에 효율적으로 수행하므로, 한 사람이 다른 사람을 차단하지 않고 아름답고 단순합니다.
도구를 호출하는 데는 비용이 들지만 성능 측면에서이를 개발할 것입니다. 이러한 도구는 C로 된 수천 개의 명령으로 작성 될 수 있습니다. 프로세스를 작성하고, 도구를로드, 초기화, 정리, 프로세스 파기 및 대기해야합니다.
호출 cut
은 부엌 서랍을 열고 칼을 가지고 사용하고 씻고 말리고 서랍에 다시 넣는 것과 같습니다. 할 때 :
while read line; do
echo $line | cut -c3
done < file
그것은 파일의 각 줄과 같 read
으며 부엌 서랍에서 도구를 가져옵니다 ( 그것에 맞게 설계되지 않았기 때문에 매우 서투른 것 ), 줄을 읽고, 읽은 도구를 씻고, 서랍에 다시 넣으십시오. 그런 다음 도구 echo
및 cut
도구에 대한 모임을 예약 하고 서랍에서 가져 와서 호출하고 씻고 말리고 서랍에 다시 넣습니다.
이러한 도구 (일부 read
하고 echo
) 대부분의 쉘에 내장되어 있습니다,하지만 거의 때문에 여기에 차이가 없습니다 echo
그리고 cut
여전히 별도의 프로세스에서 실행해야합니다.
그것은 양파를 자르는 것과 같지만 칼을 씻고 각 조각 사이의 부엌 서랍에 다시 넣으십시오.
여기서 확실한 방법은 cut
도구를 서랍에서 꺼내어 전체 양파를 썰어 작업이 완료된 후 다시 서랍에 넣는 것입니다.
쉘, 특히 텍스트를 처리하기 위해 IOW는 가능한 한 적은 유틸리티를 호출하고 작업에 협력하게하고 다음 도구를 실행하기 전에 각 도구가 시작, 실행, 정리 될 때까지 수천 개의 도구를 순서대로 실행하지 않습니다.
또한 읽기 브루스의 좋은 대답 . 쉘의 하위 수준 텍스트 처리 내부 도구 (의 경우는 제외 zsh
)는 제한적이고 번거로우 며 일반적으로 일반 텍스트 처리에 적합하지 않습니다.
공연
앞에서 언급했듯이 하나의 명령을 실행하면 비용이 발생합니다. 해당 명령이 내장되어 있지 않으면 막대한 비용이 들지만 내장되어 있어도 비용이 큽니다.
그리고 쉘은 그런 식으로 실행되도록 설계되지 않았으며, 성능이 뛰어난 프로그래밍 언어가 될 수 없습니다. 그들은 단지 명령 줄 해석 기일뿐입니다. 따라서이 부분에서는 최적화가 거의 이루어지지 않았습니다.
또한 쉘은 별도의 프로세스에서 명령을 실행합니다. 이러한 빌딩 블록은 공통 메모리 또는 상태를 공유하지 않습니다. a fgets()
또는 fputs()
C 를 수행하면 stdio의 기능입니다. stdio는 모든 stdio 기능에 대한 입력 및 출력을위한 내부 버퍼를 유지하여 값 비싼 시스템 호출을 너무 자주 수행하지 않습니다.
해당 심지어 내장 쉘 유틸리티 ( read
, echo
, printf
) 그렇게 할 수 없습니다. read
한 줄을 읽습니다. 줄 바꿈 문자를지나 읽은 경우 다음에 실행하는 명령이 누락됩니다. 따라서 read
한 번에 한 바이트 씩 입력을 읽어야합니다 (일부 구현에서는 입력이 일반 파일 인 경우 청크를 읽고 다시 검색하지만 최적화는 일반 파일에서만 작동 bash
하며 예를 들어 128 바이트 청크 만 읽습니다) 여전히 텍스트 유틸리티보다 훨씬 적습니다.)
출력 측에서와 마찬가지로 출력을 echo
버퍼링 할 수는 없으며 다음 명령을 실행하면 해당 버퍼를 공유하지 않기 때문에 즉시 출력해야합니다.
분명히, 명령을 순차적으로 실행한다는 것은 명령을 기다려야한다는 것을 의미합니다. 쉘과 도구에서 제어 할 수있는 약간의 스케줄러 댄스입니다. 또한 (파이프 라인에서 오래 실행되는 도구 인스턴스를 사용하는 것과는 대조적으로) 사용 가능한 경우 여러 프로세서를 동시에 활용할 수 없다는 것을 의미합니다.
그 while read
루프와 (어쩌면) 동등한 것 사이 cut -c3 < file
에서, 나의 빠른 테스트에서, 내 테스트에서 약 40000의 CPU 시간 비율이 있습니다 (1 초 대 반나절). 그러나 쉘 내장 만 사용하더라도 :
while read line; do
echo ${line:2:1}
done
(여기서는 bash
)로, 여전히 약 1 : 600입니다 (1 초 대 10 분).
신뢰성 / 가독성
해당 코드를 올바르게 얻는 것은 매우 어렵습니다. 내가 준 예제는 야생에서 너무 자주 보지만 많은 버그가 있습니다.
read
다양한 작업을 수행 할 수있는 편리한 도구입니다. 사용자의 입력을 읽고 단어로 분리하여 다른 변수에 저장할 수 있습니다. read line
않습니다 하지 입력 라인을 읽거나 어쩌면 그것은 매우 특별한 방법으로 행을 읽습니다. 실제로 판독 단어를 입력으로부터 분리하여 그 단어 $IFS
와 백 슬래시 여기서 세퍼레이터 또는 개행 문자를 탈출하는 데 사용될 수있다.
기본값이 다음 $IFS
과 같은 입력에서
foo\/bar \
baz
biz
read line
저장할 "foo/bar baz"
로 $line
하지, " foo\/bar \"
예상대로.
한 줄을 읽으려면 실제로 다음이 필요합니다.
IFS= read -r line
그것은 매우 직관적이지는 않지만 그렇게 된 것입니다. 포탄은 그런 식으로 사용되지 않았 음을 기억하십시오.
동일합니다 echo
. echo
시퀀스를 확장합니다. 임의 파일의 내용과 같은 임의의 내용에는 사용할 수 없습니다. printf
대신 여기 가 필요 합니다.
물론, 모두가 빠뜨릴 수있는 변수 를 인용하는 것을 잊어 버리는 것이 일반적 입니다. 그래서 더 있습니다 :
while IFS= read -r line; do
printf '%s\n' "$line" | cut -c3
done < file
이제 몇 가지주의 사항이 있습니다.
- 를 제외하고
zsh
는 적어도 GNU 텍스트 유틸리티에 문제가 없지만 입력에 NUL 문자가 포함되어 있으면 작동하지 않습니다.
- 마지막 줄 바꿈 뒤에 데이터가 있으면 건너 뜁니다.
- 루프 내에서 stdin이 리디렉션되므로 stdin에서 명령을 읽지 않도록주의해야합니다.
- 루프 내의 명령에 대해서는 성공 여부에주의를 기울이지 않습니다. 일반적으로 오류 (디스크 전체, 읽기 오류 ...) 조건은 가난보다 일반적으로 더 가난하게, 처리됩니다 올바른 해당.
위의 문제 중 일부를 해결하려면 다음과 같이됩니다.
while IFS= read -r line <&3; do
{
printf '%s\n' "$line" | cut -c3 || exit
} 3<&-
done 3< file
if [ -n "$line" ]; then
printf '%s' "$line" | cut -c3 || exit
fi
점점 가독성이 떨어지고 있습니다.
인수를 통해 명령에 데이터를 전달하거나 변수에서 출력을 검색하는 데는 여러 가지 다른 문제가 있습니다.
- 인수의 크기에 대한 제한
- NUL 문자 (텍스트 유틸리티에도 문제가 있음)
- 그들이 함께 시작할 때 인수가 옵션으로 촬영
-
(또는 +
가끔)
- 루프에서 일반적으로 사용되는 다양한 명령의 다양한 단점
expr
, test
...
- 일관성없는 방식으로 멀티 바이트 문자를 처리하는 다양한 쉘의 (제한된) 텍스트 조작 연산자
- ...
보안 고려 사항
쉘 변수 와 명령에 대한 인수로 작업을 시작 하면 광산 필드를 입력하게됩니다.
변수 인용 을 잊어 버리고 옵션 마커 의 끝을 잊어 버리고 멀티 바이트 문자가있는 로케일 (요즘 규범)에서 작업하면 조만간 버그가 발생할 수 있습니다.
루프를 사용하고 싶을 때.
TBD
yes
파일에 빨리 쓰는가?