ファイル記述子に書き込むのではなく、コマンドをファイルに直接出力するのと同じですか?
イラスト
直接ファイルに書き込む:
for i in {1..1000}; do >>x echo "$i"; done
Fdの使用:
exec 3>&1 1>x
for i in {1..1000}; do echo "$i"; done
exec 1>&3 3>&-
後者の方が効率的ですか?
exec
を使用してループの前にファイルを開くことと、ループ内のコマンドにリダイレクトを配置することの主な違いは、前者はファイル記述子を一度だけ設定する必要があり、後者はファイルを開いたり閉じたりすることです。ループの各反復。
一度実行する方が効率的ですが、ループ内で外部コマンドを実行する場合、コマンドの起動コストの違いはおそらくなくなります。 (echo
ここはおそらく組み込みなので、それは適用されません)
出力が通常のファイル以外に送信される場合(たとえば、x
が名前付きパイプである場合)、ファイルを開いたり閉じたりする動作が他のプロセスから見える可能性があるため、違いがある可能性があります行動においても。
exec
によるリダイレクトとコマンドでのリダイレクトに実際の違いはないことに注意してください。どちらもファイルを開き、ファイル記述子番号を処理します。
これらの2つは、ファイルへのopen()
とファイルへのwrite()
の両方で、ほぼ同等である必要があります。 (ただし、コマンドの実行中にfd 1が格納される方法には違いがあります。):
for i in {1..1000}; do
>>x echo "$i"
done
for i in {1..1000}; do
exec 3>&1 1>>x # assuming fd 3 is available
echo "$i" # here, fd 3 is visible to the command
exec 1>&3 3>&-
done
はい、より効率的です
テストする最も簡単な方法は、カウントを500000に増やして時間を計ることです。
> time bash s1.sh; time bash s2.sh
bash s1.sh 16,47s user 10,00s system 99% cpu 26,537 total
bash s2.sh 10,51s user 3,50s system 99% cpu 14,008 total
strace(1)が理由を明らかにします(write
+ 5 * open
+ 2 * fcntl
+ 2 * dup
+ close
ではなく、単純なwrite
があります):
ために for i in {1..1000}; do >>x echo "$i"; done
我々が得る:
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "997\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "998\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "999\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "1000\n", 5) = 5
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
exec 3>&1 1>x
きれいになりました
write(1, "995\n", 4) = 4
write(1, "996\n", 4) = 4
write(1, "997\n", 4) = 4
write(1, "998\n", 4) = 4
write(1, "999\n", 4) = 4
write(1, "1000\n", 5) = 5
ただし、違いは「FDの使用」によるものではなく、リダイレクトを行う場所によるものです。たとえば、for i in {1..1000}; do echo "$i"; done > x
2番目の例とほぼ同じパフォーマンスが得られます。
bash s3.sh 10,35s user 3,70s system 100% cpu 14,042 total
物事を要約し、このスレッドに新しい情報を追加するために、効率の高い順に4つの方法を比較します。 2つのテストシリーズに基づいて、100万回の反復の時間測定(ユーザー+ sys)によって効率を推定します。
>
ループリダイレクト(時間:100%)exec
を1回使用する(時間:〜100%)>>
を使用(時間:200%-250%)exec
を使用(時間:40%-480%)結論はこれです:
exec
と>>
のような単純なリダイレクトの使用にはsmallの違いがあります。 (シンプルな方が安い)。単一のコマンド実行レベルでは表示されませんが、繰り返し回数が多いと、違いが明らかになります。他の回答でikkachuが気づいたように、コマンドの実行の重みは違いを隠していますが。