답변:
다음 솔루션은 $1
표준 입력에서 첫 번째 매개 변수로 파일 이름으로 스크립트를 호출 한 경우 파일에서 읽습니다 .
while read line
do
echo "$line"
done < "${1:-/dev/stdin}"
대체이 ${1:-...}
소요 $1
그렇지 않으면 자신의 프로세스의 표준 입력의 파일 이름을 사용하는 정의 된 경우.
/proc/$$/fd/0
과 /dev/stdin
? 나는 후자가 더 흔하고 더 직관적 인 것처럼 보였다.
-r
하는 것이 좋습니다 . 선행 및 후행 공백을 보존하는 데 사용 합니다. read
\
while IFS= read -r line
/bin/sh
이외의 쉘을 사용하고 있습니까? bash
sh
아마도 가장 간단한 해결책은 병합 리디렉션 연산자로 stdin을 리디렉션하는 것입니다.
#!/bin/bash
less <&0
Stdin은 파일 디스크립터 0입니다. 위의 내용은 bash 스크립트로 파이프 된 입력을 less의 stdin으로 보냅니다.
<&0
이 상황에서 사용 하면 이점이 없습니다 -귀하의 예제는 그것과 함께 또는없이 작동합니다-bash 스크립트 내에서 호출하는 도구는 기본적으로 스크립트 자체와 동일한 stdin을 봅니다 (스크립트가 먼저 소비하지 않는 한).
가장 간단한 방법은 다음과 같습니다.
#!/bin/sh
cat -
용법:
$ echo test | sh my_script.sh
test
변수에 stdin 을 할당하려면 다음을 사용 STDIN=$(cat -)
하거나 단순히 STDIN=$(cat)
연산자가 필요하지 않은 것처럼 ( @ mklement0 comment에 따라 ) 사용할 수 있습니다.
표준 입력 에서 각 줄을 구문 분석하려면 다음 스크립트를 시도하십시오.
#!/bin/bash
while IFS= read -r line; do
printf '%s\n' "$line"
done
파일 또는 stdin 에서 읽으려면 (인수가없는 경우) 다음으로 확장 할 수 있습니다.
#!/bin/bash
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
노트:
--
read -r
백 슬래시 문자를 특별한 방식으로 취급하지 마십시오. 각 백 슬래시를 입력 라인의 일부로 간주하십시오.- 설정하지 않고
IFS
의 기본적으로 시퀀스 Space와 Tab라인의 시작과 끝에서 무시 (트리밍)된다.- 사용
printf
대신에이echo
라인이 단일 구성 할 때 빈 줄을 인쇄 피하기 위해-e
,-n
또는-E
. 그러나env POSIXLY_CORRECT=1 echo "$line"
이를 사용 하여 외부 GNUecho
를 실행 하는 해결 방법이 있습니다. 참조 : 어떻게 "-e"를 에코합니까?
참조 : 인수가 전달되지 않을 때 stdin을 읽는 방법은 무엇입니까? stackoverflow SE에서
[ "$1" ] && FILE=$1 || FILE="-"
수 FILE=${1:--}
있습니다. (Quibble : 환경 변수 와 이름 충돌을 피하기 위해 대문자로 된 모든 쉘 변수를 피하는 것이 좋습니다 .)
${1:--}
인 은 모든 쉘 POSIX - 같은 작업을해야하므로, POSIX 호환. 그러한 모든 쉘에서 작동하지 않는 것은 프로세스 대체 ( <(...)
)입니다. 예를 들어 bash, ksh, zsh에서는 작동하지만 대시에서는 작동하지 않습니다. 또한 실수로 문자를 먹지 않도록 명령 에 추가 -r
하는 것이 좋습니다 . 선행 및 후행 공백을 보존하기 위해 추가 합니다. read
\
IFS=
echo
라인이 구성되어있는 경우 : -e
, -n
또는 -E
그것은 표시되지 않습니다. 이 문제를 해결하려면, 당신은 사용해야합니다 printf
: printf '%s\n' "$line"
. 이전 편집에 포함하지 않았습니다…이 오류를 수정하면 편집 내용이 너무 자주 롤백됩니다 :(
.
--
첫 번째 인수가 다음 과 같은 경우 에는 쓸모가 없습니다.'%s\n'
IFS=
하고 나면 더 좋을 것 입니다 . . read
printf
echo
:)
나는 이것이 직접적인 방법이라고 생각합니다.
$ cat reader.sh
#!/bin/bash
while read line; do
echo "reading: ${line}"
done < /dev/stdin
-
$ cat writer.sh
#!/bin/bash
for i in {0..5}; do
echo "line ${i}"
done
-
$ ./writer.sh | ./reader.sh
reading: line 0
reading: line 1
reading: line 2
reading: line 3
reading: line 4
reading: line 5
read
stdin에서 읽고 기본적으로 너무 없다, 필요 에 < /dev/stdin
.
이 echo
솔루션 IFS
은 입력 스트림을 중단 할 때마다 새 줄을 추가 합니다. @fgm의 답변 은 약간 수정 될 수 있습니다 :
cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"
read
행동의 '동안 read
않는 잠재적으로 문자로 여러 개의 토큰으로 분할합니다. 에 포함 된 경우 단일 변수 이름 만 지정하는 경우 단일 토큰 $IFS
만 반환합니다 (그러나 기본적으로 트리밍 및 선행 및 후행 공백).
read
하고 $IFS
- echo
그 자체는 -n
깃발 없이 새로운 줄을 추가합니다 . "에코 유틸리티는 단일 피연산자 (` ') 문자로 구분되고 개행 문자 (`\ n') 문자로 구분 된 지정된 피연산자를 표준 출력에 기록합니다."
\n
echo
$_
\n
read
printf '%s\n'
echo
질문의 Perl 루프 는 명령 행의 모든 파일 이름 인수 또는 파일이 지정되지 않은 경우 표준 입력에서 읽습니다 . 내가 본 답변은 파일이 지정되지 않은 경우 단일 파일 또는 표준 입력을 처리하는 것으로 보입니다.
종종 UUOC ( Unless Use of cat
) 로 정확하게 정의 되기는하지만 cat
작업에 가장 적합한 도구 인 경우 가 종종 있으며 다음 중 하나 일 수 있습니다.
cat "$@" |
while read -r line
do
echo "$line"
done
이것의 유일한 단점은 하위 셸에서 실행되는 파이프 라인을 생성하므로 while
루프의 변수 할당과 같은 항목 은 파이프 라인 외부에서 액세스 할 수 없습니다. 그 bash
방법은 프로세스 대체입니다 .
while read -r line
do
echo "$line"
done < <(cat "$@")
이렇게하면 while
루프가 기본 셸에서 실행되므로 루프에 설정된 변수는 루프 외부에서 액세스 할 수 있습니다.
>>EOF\n$(cat "$@")\nEOF
. 마지막으로, quibble : Perl에서 수행 while IFS= read -r line
하는 작업에 대한 더 나은 근사치입니다 while (<>)
(앞뒤 공백을 유지하지만 Perl은 후행을 유지합니다 \n
).
OP에 주어진 코드를 가진 Perl의 행동은 인수를 전혀 또는 여러 개 취할 수 없으며 인수가 단일 하이픈 인 경우 -
stdin으로 이해됩니다. 또한 파일 이름을 항상로 사용할 수 있습니다 $ARGV
. 지금까지 주어진 답변 중 어느 것도 이러한 점에서 Perl의 행동을 모방하지 않습니다. 순수한 Bash 가능성이 있습니다. 트릭은 exec
적절하게 사용하는 것 입니다.
#!/bin/bash
(($#)) || set -- -
while (($#)); do
{ [[ $1 = - ]] || exec < "$1"; } &&
while read -r; do
printf '%s\n' "$REPLY"
done
shift
done
사용 가능한 파일 이름 $1
.
인수가 제공되지 않으면 인위적 -
으로 첫 번째 위치 매개 변수로 설정 됩니다. 그런 다음 매개 변수를 반복합니다. 매개 변수가 아닌 경우 -
파일 이름에서 표준 입력을로 리디렉션합니다 exec
. 이 리디렉션이 성공하면 루프로 while
루프합니다. 표준 REPLY
변수를 사용하고 있으며이 경우 재설정 할 필요가 없습니다 IFS
. 다른 이름을 원하면 재설정해야합니다 IFS
(물론 원하지 않고 수행중인 작업을 알고 있지 않은 경우).
while IFS= read -r line; do
printf '%s\n' "$line"
done
좀 더 정확하게...
while IFS= read -r line ; do
printf "%s\n" "$line"
done < file
IFS=
and -r
를 추가 read
하면 각 행을 수정하지 않고 읽을 수 있습니다 (앞뒤 공백 포함).
다음 코드를 시도하십시오 :
while IFS= read -r line; do
echo "$line"
done < file
read
없이 IFS=
하고 -r
, 가난한 사람들 $line
의 건강에 따옴표없이.
read -r
표기법을 싫어한다 . IMO, POSIX가 잘못했습니다. 이 옵션은 후행 백 슬래시에 대한 특별한 의미를 활성화해야하며 비활성화하지 않아야합니다. 따라서 POSIX가 존재하기 전의 기존 스크립트 -r
가 생략되어 중단되지 않습니다 . 그러나 POSIX 셸 및 유틸리티 표준의 가장 초기 버전 인 IEEE 1003.2 1992의 일부 였지만 그 이후에도 추가로 표시되었으므로 오랫동안 사라질 기회가 없습니다. 내 코드를 사용하지 않기 때문에 문제가 발생하지 않았습니다 -r
. 나는 운이 좋을 것입니다. 이것에 대해서는 무시하십시오.
-r
표준이어야 한다는 것에 정말로 동의합니다 . 사용하지 않으면 문제가 발생할 가능성이 적다는 데 동의합니다. 그러나 깨진 코드는 깨진 코드입니다. 내 편집은 $line
따옴표를 잘못 놓친 가난한 변수 에 의해 처음 시작되었습니다 . 내가있는 read
동안 나는 고쳤다 . echo
롤백되는 일종의 편집이므로 수정하지 않았습니다 . :(
.
코드 ${1:-/dev/stdin}
는 첫 번째 주장을 이해할 것입니다.
ARGS='$*'
if [ -z "$*" ]; then
ARGS='-'
fi
eval "cat -- $ARGS" | while read line
do
echo "$line"
done
이 답변 중 어느 것도 받아 들일 수 없습니다. 특히, 허용 된 답변은 첫 번째 명령 줄 매개 변수 만 처리하고 나머지는 무시합니다. 에뮬레이트하려는 Perl 프로그램은 모든 명령 행 매개 변수를 처리합니다. 따라서 허용 된 답변은 질문에 대답조차하지 않습니다. 다른 답변은 bash 확장을 사용하고 불필요한 'cat'명령을 추가하거나 입력을 출력으로 에코하는 간단한 경우에만 작동하거나 불필요하게 복잡합니다.
그러나 나는 그들이 나에게 아이디어를 줬기 때문에 그들에게 약간의 신용을 주어야한다. 완전한 대답은 다음과 같습니다.
#!/bin/sh
if [ $# = 0 ]
then
DEFAULT_INPUT_FILE=/dev/stdin
else
DEFAULT_INPUT_FILE=
fi
# Iterates over all parameters or /dev/stdin
for FILE in "$@" $DEFAULT_INPUT_FILE
do
while IFS= read -r LINE
do
# Do whatever you want with LINE here.
echo $LINE
done < "$FILE"
done
위의 모든 답변을 결합하고 내 요구에 맞는 쉘 함수를 만들었습니다. 이것은 내 두 Windows10 컴퓨터의 cygwin 터미널에서 공유 폴더가있는 곳입니다. 다음을 처리 할 수 있어야합니다.
cat file.cpp | tx
tx < file.cpp
tx file.cpp
특정 파일 이름이 지정된 경우 복사하는 동안 동일한 파일 이름을 사용해야합니다. 입력 데이터 스트림이 통해 파이프 된 경우 시간 분과 초를 가진 임시 파일 이름을 생성해야합니다. 공유 된 메인 폴더에는 요일의 하위 폴더가 있습니다. 이것은 조직을위한 것입니다.
보라, 나의 필요를위한 궁극적 인 대본 :
tx ()
{
if [ $# -eq 0 ]; then
local TMP=/tmp/tx.$(date +'%H%M%S')
while IFS= read -r line; do
echo "$line"
done < /dev/stdin > $TMP
cp $TMP //$OTHER/stargate/$(date +'%a')/
rm -f $TMP
else
[ -r $1 ] && cp $1 //$OTHER/stargate/$(date +'%a')/ || echo "cannot read file"
fi
}
이것을 더 최적화 할 수있는 방법이 있다면 알고 싶습니다.
다음은 표준 sh
( dash
Debian에서 테스트 됨)에서 작동하며 읽을 수는 있지만 맛의 문제입니다.
if [ -n "$1" ]; then
cat "$1"
else
cat
fi | commands_and_transformations
세부 사항 : 첫 번째 매개 변수가 비어 있지 cat
않으면 해당 파일, 그렇지 않으면 cat
표준 입력입니다. 그런 다음 전체 if
명령문 의 출력 이로 처리됩니다 commands_and_transformations
.
cat "${1:--}" | any_command
. 쉘 변수를 읽고 에코하는 것은 작은 파일에는 효과가 있지만 확장 성이 떨어집니다.
[ -n "$1" ]
로 단순화 될 수있다[ "$1" ]
.