回答:
次の解決策は、スクリプトが最初のパラメーターとしてファイル名を指定して呼び出された場合はファイルから読み取り、$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はファイル記述子ゼロです。上記は、bashスクリプトにパイプされた入力をlessのstdinに送信します。
<&0
この状況で使用してもメリットはありません-例はそれがあってもなくても同じように動作します-おそらく、bashスクリプト内から呼び出すツールは、デフォルトでスクリプト自体と同じstdinを参照します(スクリプトが最初にそれを消費する場合を除く)。
最も簡単な方法は次のとおりです。
#!/bin/sh
cat -
使用法:
$ echo test | sh my_script.sh
test
stdinを変数に割り当てるには、次を使用できます。STDIN=$(cat -)
または単にSTDIN=$(cat)
演算子が必要ない場合(@ mklement0のコメントに従って)。
標準入力から各行を解析するには、次のスクリプトを試してください。
#!/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
標準入力から読み込み、デフォルトではそうありません、不要のため< /dev/stdin
。
echo
ソリューションは、いつでも新しい行を追加してIFS
入力ストリームを破ります。@fgmの回答は少し変更できます。
cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"
read
の振る舞いを参照していた場合:read
は潜在的に文字によって複数のトークンに分割されます。に含まれている場合、単一の変数名のみを指定した場合にのみ単一のトークン$IFS
が返されます(ただし、デフォルトでは先頭と末尾の空白が削除されます)。
read
およびの動作について100%同意します$IFS
- echo
それ自体が-n
フラグなしで新しい行を追加します。"エコーユーティリティは、単一の空白(` ')文字で区切られ、その後に改行( `\ n')文字が続く指定されたオペランドを標準出力に書き込みます。"
\n
echo
$_
\n
read
printf '%s\n'
echo
問題のPerlループは、コマンドラインのすべてのファイル名引数から、またはファイルが指定されていない場合は標準入力から読み取ります。ファイルが指定されていない場合、すべての回答で単一のファイルまたは標準入力が処理されるようです。
多くの場合、UUOC(Useless Use of cat
)として正確に欺かれますが、がcat
そのジョブに最適なツールである場合があり、これがその1つであると主張できます。
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:while IFS= read -r line
は、while (<>)
Perlでの動作のより良い近似です(先頭と末尾の空白は保持されますが、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=
と-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
ロールバックされる編集の種類なので、修正しませんでした。:(
。
これらの答えはどれも受け入れられません。特に、受け入れられた回答は最初のコマンドラインパラメータのみを処理し、残りは無視します。エミュレートしようとしている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
上記の答えをすべて組み合わせて、自分のニーズに合ったシェル関数を作成しました。これは、2台の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" ]
。
いかがですか
for line in `cat`; do
something($line);
done
cat
コマンドラインに配置されます。コマンドラインには最大サイズがあります。また、これは行ごとではなく、単語ごとに読み取ります。